【问题标题】:Django: a class based view with mixins and dispatch methodDjango:具有混合和调度方法的基于类的视图
【发布时间】:2015-03-05 15:02:11
【问题描述】:

通常,我使用基于类的视图的dispatch 方法来设置一些初始变量或添加一些基于用户权限的逻辑。

例如,

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

按预期工作:当匿名用户访问页面时,调用LoginRequiredMixindispatch 方法,然后将用户重定向到登录页面。

但是如果我想为这个视图添加一些权限或者设置一些初始变量,例如,

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)

在某些情况下它不起作用,因为尚未调用视图继承的 mixins 的 dispatch 方法。因此,例如,为了能够请求用户的权限,我必须从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)

这个例子很简单,但有时 mixin 中有一些更复杂的逻辑:它检查权限、进行一些计算并将其存储在类属性中等等。

现在我通过从 mixin 复制一些代码(如上面的示例)或通过将代码从视图的 dispatch 方法复制到另一个 mixin 并在第一个 mixin 之后继承它来解决它以执行它们order(这不是很漂亮,因为这个新的 mixin 只被一个视图使用)。

有什么合适的方法可以解决这类问题吗?

【问题讨论】:

  • 第一次致电super(GenerateReportView, self).dispatch(*args, **kwargs)。然后在您覆盖的调度中完成其余的工作
  • @MihaiZamfir 它不起作用,因为例如LoginRequiredMixin 返回一个HttpResponseRedirect 对象,所以如果我们将它存储在一个变量中,它不会重定向到登录视图直到我们在dispatch 方法结束时返回这个变量。
  • 但您可以检查响应是否为重定向,然后继续调度
  • 问题是混入的上游处理可能会导致有效的重定向。例如。有像(LoginRequiredMixin,CustomViewWhichRedirectsSometimes)这样的继承层次结构,现在,如果需要登录的调度返回重定向,我们需要以某种方式判断是因为用户未授权还是完全预期的来自 CustomViewWhichRedirectsSometimes 的重定向。

标签: django django-views mixins


【解决方案1】:

对于您给出的示例,我将使用来自django-bracesUserPassesTestMixin

class GenerateReportView(UserPassesTestMixin, FormView):
    def test_func(self, user):
        return user.is_superuser or user.is_manager

如果这不适合您更复杂的逻辑,那么创建一个单独的 mixin 听起来不错,因为它很好地封装了复杂的逻辑。

编辑
从 django 1.9 开始,UserPassesTestMixin 现在包含在 django 中:https://docs.djangoproject.com/en/1.11/topics/auth/default/#django.contrib.auth.mixins.UserPassesTestMixin

【讨论】:

    【解决方案2】:

    我会编写自定义类,检查所有权限

    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)
    

    GenerateReportView(SuperOrManagerPermissionsMixin, FormView) 类的实现不需要重写调度方法

    如果您使用多重继承并且其中一个父类需要改进,最好先改进它。它使代码更干净。

    【讨论】:

      【解决方案3】:

      这是一篇旧帖子,但其他人可能会遇到,所以这是我提出的解决方案。

      当你说

      "[...]我想为此视图添加一些权限或设置一些 初始变量,例如[...]"

      您可以编写一个单独的方法来设置这些变量,而不是在视图的调度方法中设置这些初始变量,然后在您的 get (并在需要时发布)方法中调用该方法。它们在调度后调用,因此设置初始变量不会与混入中的调度冲突。 所以重写方法

      def set_initial_variables():
          self.hey = something
          return 
      
      def get(blablabla):
          self.set_initial_variables()
          return super(blabla, self).get(blabla)
      

      这可能比在视图的调度中复制和粘贴 mixin 的代码更干净。

      【讨论】:

        【解决方案4】:

        可以使用 Django UserPassesTestMixin mixin 或 @user_passes_test 装饰器来完成。

        UserPassesTestMixin 示例

        from django.contrib.auth.mixins import UserPassesTestMixin
        
        
        class SuperUserOrManagerRequiredMixin(UserPassesTestMixin):
            def test_func(self):
                if self.request.user.is_superuser or self.request.user.is_manager:
                    return True
        
                return False
        
        
        class MyView(LoginRequiredMixin, SuperUserOrManagerRequiredMixin, View):
            ...
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-05-29
          • 2011-08-10
          • 1970-01-01
          • 1970-01-01
          • 2018-08-21
          • 1970-01-01
          • 2022-01-07
          • 2012-12-28
          相关资源
          最近更新 更多