Django中的非全局中间件

51 投票
9 回答
23401 浏览
提问于 2025-04-15 23:12

在Django框架中,有一个设置文件用来定义每次请求时要运行的中间件。这个中间件的设置是全局的,也就是说它会对所有请求都生效。有没有办法让某些特定的视图使用一组不同的中间件呢?我想让特定的URL使用一套和全局设置不同的中间件。

9 个回答

6

这是我最近用来解决你在Ned的回答下评论中提到的情况的一个方法...

这个方法假设:

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

B) 你的逻辑可以等到process_view再执行,而不是在process_request时,因为在process_view中,你可以在参数view_func被解析后进行检查。(或者你可以根据Ignacio的建议调整下面的代码,使用urlresolvers。)

# 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邮件列表中讨论过,那会是个不错的讨论主题。

7

我有一个真正的解决办法来处理这个问题。需要注意的是,这个方法有点小技巧。

""" 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

补充:我已经删除了之前那个会运行两次视图的版本。

63

你需要的是 decorator_from_middleware

from django.utils.decorators import decorator_from_middleware

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

这个东西不是针对网址的,而是针对每个视图的,所以你可以更细致地控制它的效果。

撰写回答