【问题标题】:Non-global middleware in DjangoDjango 中的非全局中间件
【发布时间】:2011-02-24 09:48:54
【问题描述】:

在 Django 中有一个设置文件,它定义了要在每个请求上运行的中间件。此中间件设置是全局的。有没有办法在每个视图的基础上指定一组中间件?我想让特定的 url 使用一组不同于全局集的中间件。

【问题讨论】:

    标签: python django middleware django-middleware


    【解决方案1】:

    你想要decorator_from_middleware

    from django.utils.decorators import decorator_from_middleware
    
    @decorator_from_middleware(MyMiddleware)
    def view_function(request):
        #blah blah
    

    它不适用于 URL,但它适用于每个视图,因此您可以对其效果进行细粒度控制。

    【讨论】:

    • 好的,但是如果我想排除中间件而不是附加它们怎么办。例如,我的设置文件列出了中间件 MIDDLEWARE_CLASSES = ('A', 'B', 'C') 并且我希望一个视图具有 A 和 B 而不是 C。是否有装饰器可以删除中间件?只有一个 Django 应用程序需要这个自定义中间件,因此我不想在我的应用程序的每个其他视图中添加 decorator_from_middleware
    • @csrf_exempt,它的作用类似于您所要求的,通过在视图上设置一个标志来工作,然后由相应的 CSRF 中间件检查。当然,这不是一个通用的解决方案,但只是注意。
    • 如何在 Views 中导入“MyMiddleware”类?尝试从 myapp.middleware.myFile import * 但它没有被拾起。写了一个问题:stackoverflow.com/q/52927077/6163866
    【解决方案2】:

    我对这个问题有一个真正的解决方案。警告;这有点小技巧。

    """ Allows short-curcuiting of ALL remaining middleware by attaching the
    @shortcircuitmiddleware decorator as the TOP LEVEL decorator of a view.
    
    Example settings.py:
    
    MIDDLEWARE_CLASSES = (
        'django.middleware.common.CommonMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
    
        # THIS MIDDLEWARE
        'myapp.middleware.shortcircuit.ShortCircuitMiddleware',
    
        # SOME OTHER MIDDLE WARE YOU WANT TO SKIP SOMETIMES
        'myapp.middleware.package.MostOfTheTimeMiddleware',
    
        # MORE MIDDLEWARE YOU WANT TO SKIP SOMETIMES HERE
    )
    
    Example view to exclude from MostOfTheTimeMiddleware (and any subsequent):
    
    @shortcircuitmiddleware
    def myview(request):
        ...
    
    """
    
    def shortcircuitmiddleware(f):
        """ view decorator, the sole purpose to is 'rename' the function
        '_shortcircuitmiddleware' """
        def _shortcircuitmiddleware(*args, **kwargs):
            return f(*args, **kwargs)
        return _shortcircuitmiddleware
    
    class ShortCircuitMiddleware(object):
        """ Middleware; looks for a view function named '_shortcircuitmiddleware'
        and short-circuits. Relies on the fact that if you return an HttpResponse
        from a view, it will short-circuit other middleware, see:
        https://docs.djangoproject.com/en/dev/topics/http/middleware/#process-request
         """
        def process_view(self, request, view_func, view_args, view_kwargs):
            if view_func.func_name == "_shortcircuitmiddleware":
                return view_func(request, *view_args, **view_kwargs)
            return None
    

    编辑:删除了运行视图两次的先前版本。

    【讨论】:

      【解决方案3】:

      这是我最近用来解决您在对 Ned 的回答的评论中提出的场景的解决方案...

      假设:

      A) 这是一个自定义中间件,或者您可以使用自己的中间件类扩展/包装的中间件

      B) 您的逻辑可以等到process_view 而不是process_request,因为在process_view 中,您可以在解决view_func 参数后对其进行检查。 (或者您可以调整下面的代码以使用 urlresolvers,如 Ignacio 所示)。

      # settings.py
      EXCLUDE_FROM_MY_MIDDLEWARE = set('myapp.views.view_to_exclude', 
          'myapp.views.another_view_to_exclude')
      
      # some_middleware.py
      
      from django.conf import settings
      
      def process_view(self, request, view_func, view_args, view_kwargs):
          # Get the view name as a string
          view_name = '.'.join((view_func.__module__, view_func.__name__))
      
          # If the view name is in our exclusion list, exit early
          exclusion_set = getattr(settings, 'EXCLUDE_FROM_MY_MIDDLEWARE', set())
          if view_name in exclusion_set:
              return None
      
          # ... middleware as normal ...
          #
          # Here you can also set a flag of some sort on the `request` object
          # if you need to conditionally handle `process_response` as well.
      

      可能有一种方法可以进一步概括这种模式,但这很好地实现了我的目标。

      为了回答您更一般的问题,我认为 Django 库中目前没有任何东西可以帮助您解决这个问题。如果 django-users 邮件列表尚未解决,这将是一个很好的主题。

      【讨论】:

        【解决方案4】:

        您可以使用 process_view 方法,该方法在调用视图函数之前调用。在 process_view 你可以检查——这个视图是否需要这个中间件拦截。

        【讨论】:

          【解决方案5】:

          在中间件的包装器中使用django.core.urlresolvers.resolve()request.path 来尝试查看视图是否在应用程序内,如果是则跳过处理。

          【讨论】:

          • 所以我必须在我的中间件代码中使用 if 语句来强制它在某些应用程序中跳过?
          • 在包装器中,而不是中间件本身。
          • 中间件包装器的例子是什么?
          • docs.djangoproject.com/en/dev/topics/http/middleware/… 只需调用中间件包装器中的实际中间件方法即可。
          • @IgnacioVazquez-Abrams 我也想知道您所说的中间件包装器是什么意思。
          【解决方案6】:

          我能找到的最好的方法是使用 if request.path_info.startswith('...') 通过返回请求来跳过中间件。现在,您可以创建中间件只是为了跳过然后继承它。也许您可以做一些更简单的事情并将该列表保存在您的 settings.py 中,然后跳过所有这些。如果我有任何错误,请告诉我。

          【讨论】:

            【解决方案7】:

            我认为这是从中间件中排除视图的简单方法

             from django.core.urlresolvers import resolve
             current_url = resolve(request.path_info).url_name
            
             if want to exclude url A,
            
             class your_middleware:
                def process_request(request):
                    if not current_url == 'A':
                        "here add your code"
            

            【讨论】:

              【解决方案8】:

              Django urlmiddleware 允许仅将中间件应用于映射到特定 url 的视图。

              【讨论】:

              【解决方案9】:
              #settings.py
              EXCLUDE_FROM_MY_MIDDLEWARE =set({'custom_app.views.About'})
              
              #middlware.py
              from django.conf import settings
              
              class SimpleMiddleware(object):
              
                   def __init__(self,get_response):
                        self.get_response=get_response
                        
                   
                   def __call__(self,request):
                        
                        response = self.get_response(request)
                        return response
              
                   def process_view(self,request, view_func, view_args, view_kwargs):
                       
                        view_function='.'.join((view_func.__module__,view_func.__name__))
                        exclusion_set=getattr(settings,'EXCLUDE_FROM_MY_MIDDLEWARE',set() )
                        if view_function in exclusion_set:
                             return None
                        
                        print("continue for others views")
                        
                   def process_exception(self,request, exception):
                        return HttpResponse(exception)
              

              【讨论】:

              • 欢迎来到 SO。请考虑解释您的答案。会更有帮助。
              猜你喜欢
              • 1970-01-01
              • 2015-12-24
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多