Skip to main content

Django Middleware for Beginers

We will be disscussing the points:

1. What is a middleware
2. When to use middleware
3. Things to remember when writing middleware
4. Writing some middlewares to understand how order of middleware matters

What is a middleware

Middlewares are hooks to modify Django request or response object. Putting the definition of middleware from Django docs.
Middleware is a framework of hooks into Djangos request/response processing. Its a light, low-level plugin system for globally altering Djangos input or output.

When to use middleware

You can use middleware if you want to modify the request i.e HttpRequest object which is sent to the view. Or you might want to modify the HttpResponse object returned from the view. Both these can be achieved by using a middleware.
You might want to perform an operation before the view executes. In such case you would use a middleware.
Django provides some default middleware. eg: AuthenticationMiddleware
Very often you would have used request.user inside the view. Django wants user attribute to be set on request before any view executes. Django takes a middleware approach to accomplish this. So Django provides an AuthenticationMiddleware which can modify the request object.
And Django modifies the request object like:
https://github.com/django/django/blob/master/django/contrib/auth/middleware.py#L22
Similarly you might have an application which works with users of different timezones. You want to use the user's timezone while showing any page to the user. You want access to user's timezone in all the views. It makes sense to add it in session in such case. So you can add a middleware like this:
class TimezoneMiddleware(object):
    def process_request(self, request):
        # Assuming user has a OneToOneField to a model called Profile
        # And Profile stores the timezone of the User.
        request.session['timezone'] = request.user.profile.timezone
TimezoneMiddleware is dependent on request.user. And request.user is populated in AuthenticationMiddleware. So TimezoneMiddleware written by us must come after Django provided AuthenticationMiddleware in the tuple settings.MIDDLEWARE_CLASSES.
We will get more idea about order of middlewares in coming examples.

Things to remember when using middleware

  • Order of middlewares is important.
  • A middleware only need to extend from class object.
  • A middleware is free to implement some of the methods and not implement other methods.
  • A middleware may implement process_request but may not implement process_response and process_view. Infact it is very common and lot of Django provided middlewares do it.
  • A middleware may implement process_response but not implement process_request.
AuthenticationMiddleware only implements process_request and doesn't implement process_response. You can check it here
GZipMiddleware only implements process_response and doesn't implement process_request or process_view. You can see it here

Writing some middlewares

Make sure you have a Django project with a url and a view, and that you are able to access that view. Since we will try several things with request.user, make sure authentication is properly set for you and that request.user prints the right thing in this view.
Create a file middleware.py in any of your app.
I have an app called books and so I am writing this in books/middleware.py
class BookMiddleware(object):
    def process_request(self, request):
        print "Middleware executed"
Add this middleware in MIDDLEWARE_CLASSES
MIDDLEWARE_CLASSES = (
    'books.middleware.BookMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
Make request to any url. This should get printed on runserver console
Middleware executed
Modify BookMiddleware.process_request so it looks like
class BookMiddleware(object):
    def process_request(self, request):
        print "Middleware executed"
        print request.user
Make request to a url again. This will raise an error.
'WSGIRequest' object has no attribute 'user'
This happened because attribute user hasn't been set on request yet.
Now change the order of middlewares so BookMiddleware comes after AuthenticationMiddleware
MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'books.middleware.BookMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
Make request to any url. This should get printed on runserver console
Middleware executed
<username>
This tells that process_request is executed on the middlewares in the order in which they are listed in settings.MIDDLEWARE_CLASSES
You can verify it further. Add another middleware in your middleware.py
class AnotherMiddleware(object):
    def process_request(self, request):
        print "Another middleware executed"
Add this middleware in MIDDLEWARE_CLASSES too.
MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'books.middleware.BookMiddleware',
    'books.middleware.AnotherMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
Now output would be:
Middleware executed
<username>
Another middleware executed

How returning HttpResponse from process_request changes things

Modify BookMiddleware so it looks like
class BookMiddleware(object):
    def process_request(self, request):
        print "Middleware executed"
        print request.user
        return HttpResponse("some response")
Try any url now and your output would be:
Middleware executed
<username>
You will notice two things:
  • Your view will no more be executed and no matter which url you try, you will see "some response".
  • AnotherMiddleware.process_request will not be executed anymore.
So if a Middleware's process_request() returns a HttpResponse object then process_request of any subsequent middlewares is bypassed. Also view execution is bypassed. You would rarely do this or require this in your projects.
Comment "return HttpResponse("some response")" so process_request of both middlewares keep executing.

Working with process_response

Add method process_response to both the middlewares
class AnotherMiddleware(object):
    def process_request(self, request):
        print "Another middleware executed"

    def process_response(self, request, response):
        print "AnotherMiddleware process_response executed"
        return response
class BookMiddleware(object):
    def process_request(self, request):
        print "Middleware executed"
        print request.user
        return HttpResponse("some response")
        #self._start = time.time()

    def process_response(self, request, response):
        print "BookMiddleware process_response executed"
        return response
Try some url. Output would be
Middleware executed
<username>
Another middleware executed
AnotherMiddleware process_response executed
BookMiddleware process_response executed
AnotherMiddleware.process_response() is executed before BookMiddleware.process_response() while AnotherMiddleware.process_request() executes after BookMiddleware.process_request(). So process_response() follows the reverse of what happens with process_request. process_response() is executed for last middleware then second last middleware and so on till the first middleware.

process_view

Django applies middleware's process_view() in the order it’s defined in MIDDLEWARE_CLASSES, top-down. This is similar to the order followed for process_request().
Also if any process_view() returns an HttpResponse object, then subsequent process_view() calls are bypassed and not executed.

Writing our own middleware

We want to write a simple middleware that just tells the most expensive functions/methods and time it took to execute those functions. We don't want to capture sql queries or anything fancy.
We will use standard Python provided cProfile to achieve our goal. This official doc can help you get familiar with cProfile in 10 mins.
Add the following in any app's middleware.py. Supposing you have an app called books and you add this in books/middleware.py
import cProfile, pstats, StringIO
class ProfilerMiddleware(object):
    def process_request(self, request):
        pr = cProfile.Profile()
        pr.enable()
        request._pr = pr

    def process_response(self, request, response):
        request._pr.disable()
        s = StringIO.StringIO()
        sortby = 'cumulative'
        # Sort the output by cumulative time it took in fuctions/methods.
        ps = pstats.Stats(request._pr, stream=s).sort_stats(sortby)
        # Print only 10 most time consuming functions
        ps.print_stats(10)
        print s.getvalue()
        return response
And add books.middleware.ProfileMiddleware at top of your MIDDLEWARE_CLASSES.
MIDDLEWARE_CLASSES = (
    'books.middleware.ProfilerMiddleware',
    # Assuming you have some custom middlewares here, even they will be profiled
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    # This middleware will be profiled too.
    # 'books.middleware.SomeCustomMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
Try any url and you should see the profiler output on the runserver console.

Explanation

  • We put our middleware at top of MIDDLEWARE_CLASSES.
  • So this middleware's process_request() will be executed before any other middleware's process_request(). Also it will be executed before any other middleware's any other function like process_view() etc.
  • We enable profiling in process_request() so everything hereafter will be profiled. So process_request() and process_view() of any other middleware will be profiled.
  • We disable profiling in process_response() of our middleware. process_response() of this middleware will run at last, i.e after process_response() of all other middlewares have run.
  • This way process_response() of all other middlewares get profiled too.


Popular posts from this blog

How to read or extract text data from passport using python utility.

Hi ,  Lets get start with some utility which can be really helpful in extracting the text data from passport documents which can be images, pdf.  So instead of jumping to code directly lets understand the MRZ, & how it works basically. MRZ Parser :                 A machine-readable passport (MRP) is a machine-readable travel document (MRTD) with the data on the identity page encoded in optical character recognition format Most travel passports worldwide are MRPs.  It can have 2 lines or 3 lines of machine-readable data. This method allows to process MRZ written in accordance with ICAO Document 9303 (endorsed by the International Organization for Standardization and the International Electrotechnical Commission as ISO/IEC 7501-1)). Some applications will need to be able to scan such data of someway, so one of the easiest methods is to recognize it from an image file. I 'll show you how to retrieve the MRZ infor...

How to generate class diagrams pictures in a Django/Open-edX project from console

A class diagram in the Unified Modeling Language ( UML ) is a type of static structure diagram that describes the structure of a system by showing the system’s classes, their attributes, operations (or methods), and the relationships among objects. https://github.com/django-extensions/django-extensions Step 1:   Install django extensions Command:  pip install django-extensions Step 2:  Add to installed apps INSTALLED_APPS = ( ... 'django_extensions' , ... ) Step 3:  Install diagrams generators You have to choose between two diagram generators: Graphviz or Dotplus before using the command or you will get: python manage.py graph_models -a -o myapp_models.png Note:  I prefer to use   pydotplus   as it easier to install than Graphviz and its dependencies so we use   pip install pydotplus . Command:  pip install pydotplus Step 4:  Generate diagrams Now we have everything installed...

How to Remove course from Open-edX

Go to vagrant  => 1. In the edx-platform directory:  - cd /edx/app/edxapp/edx-platform 2. Run the following Django management command:   - sudo -u www-data /edx/bin/python.edxapp /edx/bin/manage.edxapp lms dump_course_ids --settings aws    - sudo -u www-data /edx/bin/python.edxapp /edx/bin/manage.edxapp lms dump_course_ids --settings=devstack 3. Find the course ID which you'd like to delete in the resulting list of course IDs. 4. Copy the course ID into the following command and run it:  - sudo -u www-data /edx/bin/python.edxapp /edx/bin/manage.edxapp cms delete_course <COURSE_ID> --settings aws  -   sudo -u www-data /edx/bin/python.edxapp /edx/bin/manage.edxapp cms delete_course <COURSE_ID> --settings=devstack  - You'll be asked to verify the deletion . To verify the deletion, run the command from step 2 above and ensure that the course ID is not in the list. Help reference : ...