关于python:Django中的非全局中间件

Non-global middleware in Django

在Django中,有一个设置文件,用于定义要在每个请求上运行的中间件。 此中间件设置是全局的。 有没有一种方法可以针对每个视图指定一组中间件? 我想让特定的URL使用一组不同于全局集的中间件。


您需要decorator_from_middleware

1
2
3
4
5
from django.utils.decorators import decorator_from_middleware

@decorator_from_middleware(MyMiddleware)
def view_function(request):
    #blah blah

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


对于这个问题,我有一个真正的解决方案。警告;这有点骇人听闻。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
""" 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

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


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

它假定:

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

B)您的逻辑可以等到process_view而不是process_request,因为在process_view中,您可以在解析完view_func参数后检查它。 (或者您也可以按照Ignacio的说明调整以下代码以使用urlresolvers)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 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邮件列表尚未在此处解决,它将是一个很好的主题。


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


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


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


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

1
2
3
4
5
6
7
8
9
 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"

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