vistas - Django: una vista basada en clase con mixins y método de envío
django forms widgets (3)
Normalmente, utilizo un método de dispatch
de una vista basada en clase para establecer algunas variables iniciales o agregar alguna lógica basada en los permisos del usuario.
Por ejemplo,
from django.views.generic import FormView
from braces.views import LoginRequiredMixin
class GenerateReportView(LoginRequiredMixin, FormView):
template_name = ''reporting/reporting_form.html''
form_class = ReportForm
def get_form(self, form_class):
form = form_class(**self.get_form_kwargs())
if not self.request.user.is_superuser:
form.fields[''report_type''].choices = [
choice for choice in form.fields[''report_type''].choices
if choice[0] != INVOICE_REPORT
]
return form
Funciona como se esperaba: cuando un usuario anónimo visita una página, se llama al método de dispatch
de LoginRequiredMixin y, a continuación, redirige al usuario a la página de inicio de sesión.
Pero si quiero agregar algunos permisos para esta vista o establecer algunas variables iniciales, por ejemplo,
class GenerateReportView(LoginRequiredMixin, FormView):
def dispatch(self, *args, **kwargs):
if not (
self.request.user.is_superuser or
self.request.user.is_manager
):
raise Http404
return super(GenerateReportView, self).dispatch(*args, **kwargs)
en algunos casos no funciona, porque los métodos de dispatch
de los mixins, que la vista hereda, no se han llamado todavía. Entonces, por ejemplo, para poder solicitar permisos de usuario, tengo que repetir la validación de LoginRequiredMixin
:
class GenerateReportView(LoginRequiredMixin, FormView):
def dispatch(self, *args, **kwargs):
if self.request.user.is_authenticated() and not (
self.request.user.is_superuser or
self.request.user.is_manager
):
raise Http404
return super(GenerateReportView, self).dispatch(*args, **kwargs)
Este ejemplo es simple, pero a veces hay una lógica más complicada en una mezcla: comprueba los permisos, hace algunos cálculos y los almacena en un atributo de clase, etc.
Por ahora lo resuelvo copiando un código de la mezcla (como en el ejemplo anterior) o copiando el código del método de dispatch
de la vista a otra combinación y heredándolo después de la primera para ejecutarlos en orden (lo que no es que bonito, porque esta nueva mezcla es usada solo por una vista).
¿Hay alguna manera adecuada para resolver este tipo de problemas?
Escribiría una clase personalizada, que verifique todos los permisos.
from django.views.generic import FormView
from braces.views import AccessMixin
class SuperOrManagerPermissionsMixin(AccessMixin):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated():
return self.handle_no_permission(request)
if self.user_has_permissions(request):
return super(SuperOrManagerPermissionsMixin, self).dispatch(
request, *args, **kwargs)
raise Http404 #or return self.handle_no_permission
def user_has_permissions(self, request):
return self.request.user.is_superuser or self.request.user.is_manager
# a bit simplyfied, but with the same redirect for anonymous and logged users
# without permissions
class SuperOrManagerPermissionsMixin(AccessMixin):
def dispatch(self, request, *args, **kwargs):
if self.user_has_permissions(request):
return super(SuperOrManagerPermissionsMixin, self).dispatch(
request, *args, **kwargs)
else:
return self.handle_no_permission(request)
def user_has_permissions(self, request):
return request.user.is_authenticated() and (self.request.user.is_superuser
or self.request.user.is_manager)
class GenerateReportView(SuperOrManagerPermissionsMixin, FormView):
#Remove next two lines, don''t need it
def dispatch(self, *args, **kwargs):
#or put some logic here
return super(GenerateReportView, self).dispatch(*args, **kwargs)
Y la implementación de la clase GenerateReportView (SuperOrManagerPermissionsMixin, FormView) no requiere el método de envío de anulación
Si usa herencia múltiple y una de las clases padre necesita alguna mejora, es bueno mejorarla primero. Mantiene el código más limpio.
Este es un post antiguo, pero otras personas podrían encontrarlo, así que aquí está mi solución propuesta.
Cuando tu dices
"[...] Quiero agregar algunos permisos para esta vista o establecer algunas variables iniciales , por ejemplo [...]"
En lugar de configurar esas variables iniciales en el método de envío de su vista, puede escribir un método separado para configurar esas variables y luego llamar ese método en su método de obtener (y publicar si es necesario). Se llaman después del envío, por lo que configurar sus variables iniciales no chocará con el envío en sus mixins. Así que anula el método
def set_initial_variables():
self.hey = something
return
def get(blablabla):
self.set_initial_variables()
return super(blabla, self).get(blabla)
Probablemente esto sea más limpio que copiar y pegar el código de su mezcla en el envío de su vista.
Para el ejemplo que dio, usaría UserPassesTestMixin
de django-braces .
class GenerateReportView(UserPassesTestMixin, FormView):
def test_func(self, user):
return user.is_superuser or user.is_manager
Si eso no es adecuado para su lógica más complicada, entonces crear una mezcla separada suena como un enfoque correcto, ya que encapsula la lógica complicada muy bien.
EDITAR
A partir de django 1.9, UserPassesTestMixin ahora se incluye en django: https://docs.djangoproject.com/en/1.11/topics/auth/default/#django.contrib.auth.mixins.UserPassesTestMixin