views:

146

answers:

3

In Django there is a settings file that defines the middleware to be run on each request. This middleware setting is global. Is there a way to specify a set of middleware on a per-view basis? I want to have specific urls use a set of middleware different from the global set.

+3  A: 

You want decorator_from_middleware.

from django.utils.decorators import decorator_from_middleware

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

It doesn't apply to URLs, but it works per-view, so you can have fine-grained control over its effect.

Ned Batchelder
Ok, but what if I want to exclude middleware instead of append them. For example, my settings file lists middleware MIDDLEWARE_CLASSES = ('A', 'B', 'C') and I want one view to have A and B but not C. Is there a decorator to remove middleware? This custom middleware stuff is needed in only one Django app hence I don't want to have to add `decorator_from_middleware` to every other view in my application.
hekevintran
A: 

Use django.core.urlresolvers.resolve() against request.path in a wrapper for the middleware to try to see if the view is within the app, and skip processing if so.

Ignacio Vazquez-Abrams
So I must use an if statement in my middleware code to force it skip in certain apps?
hekevintran
In the wrapper, not the middleware itself.
Ignacio Vazquez-Abrams
What is an example of a wrapper for middleware?
hekevintran
http://docs.djangoproject.com/en/dev/topics/http/middleware/#writing-your-own-middleware Just call the actual middleware methods in the middleware wrapper.
Ignacio Vazquez-Abrams
A: 

Here's a solution I used recently to address the scenario you presented in a comment to Ned's answer...

It assumes that:

A) this is a custom middleware or one that you can extend/wrap with your own middleware class

B) your logic can wait until process_view instead of process_request, because in process_view you can inspect the view_func parameter after it's been resolved. (Or you can adjust the code below to use urlresolvers as indicated by 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.

There may be a way to generalize this pattern further, but this accomplished my goal fairly well.

To answer your more general question, I don't think there is anything in the Django libraries to help you out with this currently. Would be a good topic for the django-users mailing list if it hasn't already been addressed there.

Joe Holloway