widgets vistas form fields example clases basadas django django-views mixins

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