Non-global middleware in Django
Asked Answered
N

9

51

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.

Nephrectomy answered 26/5, 2010 at 21:30 Comment(0)
E
63

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.

Electro answered 26/5, 2010 at 22:3 Comment(4)
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.Nephrectomy
@csrf_exempt, which does something similar to what you're asking, works by setting a flag on the view which is then checked by the corresponding CSRF middleware. Not a general solution, of course, but just noting.Amortize
How do you import the "MyMiddleware" class in Views? Tried from myapp.middleware.myFile import * but it is not picked up. Wrote a question:https://mcmap.net/q/354976/-can-39-t-import-custom-middleware-whilst-using-decorator_from_middleware-non-global-middleware/6163866Yamada
How is this excluding the middleware to be only used for a particular view function, because I tried this and the middleware is being executed twice.Jacobson
B
7

I have a real solution for this issue. Warning; it's a little bit of a hack.

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

Edit: removed previous version that ran the view twice.

Boniface answered 31/8, 2011 at 17:9 Comment(0)
S
6

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.

Shaniceshanie answered 26/5, 2010 at 22:47 Comment(0)
M
2

The best thing I've been able to find is using if request.path_info.startswith('...') to skip over the middleware by just returning the request. Now, you could create middleware just for the sake of skipping and then inherit that. Maybe you could do something even simpler and save that list in your settings.py and then skip all those. If I'm wrong in any way, let me know.

(Note: from what I remember, I was using Django 1.2 at this time)

Maggy answered 22/8, 2013 at 22:7 Comment(0)
P
2

You can use process_view method, that is called before calling the view func. In process_view you can check — if this view requires this middleware interception.

Pelaga answered 13/10, 2015 at 14:3 Comment(0)
A
1

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.

Aureaaureate answered 26/5, 2010 at 22:14 Comment(5)
So I must use an if statement in my middleware code to force it skip in certain apps?Nephrectomy
In the wrapper, not the middleware itself.Aureaaureate
What is an example of a wrapper for middleware?Nephrectomy
docs.djangoproject.com/en/dev/topics/http/middleware/… Just call the actual middleware methods in the middleware wrapper.Aureaaureate
@IgnacioVazquez-Abrams I'd like to know what you meant by middleware wrapper as well.Incommensurable
B
1

I think this is the easy way to exclude a view from middleware

 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"
Beadle answered 23/1, 2018 at 7:1 Comment(0)
I
0

Django urlmiddleware allows to apply middleware only to views that are mapped to specific urls.

Injured answered 11/12, 2012 at 18:49 Comment(1)
Nice app, however, it still adds global middleware that checks the requested url agains any configured url specific middleware: github.com/d0ugal/django-urlmiddleware/blob/master/…Rhnegative
H
0
#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)
Hack answered 12/2, 2021 at 7:37 Comment(1)
Welcome to SO. Please consider explaining your answer. It will be more helpful.Gregor

© 2022 - 2024 — McMap. All rights reserved.