3

I'm Django starter. So far I learned pass variable from view to template. But now I need pass variable to my main layout. I can pass it in each page's def in view. But its too much duplication. So I started learn about middleware.

I created middlewares.py and included it in settings. In middlewares.py file, how to pass variable to my main layout?

Below is my current middlewares.py content, I tried many ways and commented them out, because not working.

from django.db import connections
from django.shortcuts import render, redirect

class NotificationsMiddleware(object):
    def process_view(self, request, view_func, view_args, view_kwargs):

        request.context_data['notification_count'] = 2
        response = view_func(request, *view_args, **view_kwargs)

        return response

    # def process_request(self, request):
    #     request.notification_count = 2
    #     return

    # def process_template_response(self, request, response):
    #     # response.context['notification_count'] = 2
    #     response.context_data['notification_count'] = 2
    #     # return response
    #     return render(request, 'main/locations.html')
Gereltod
  • 2,043
  • 8
  • 25
  • 39
  • 1
    I'm not understanding why you accepted an answer that provides the solution in a `context processor`, you asked specifically how to do this in `middleware` – ViaTech May 15 '19 at 15:20
  • Like I said I was starter meantime :) – Gereltod May 16 '19 at 00:33
  • 1
    Yeah, I got that and no worries. Haha this was just a perfect post title to solve my issue recently. I figured it out through the docs, but I was not expecting a Context Procesor to be the main solution here. – ViaTech May 16 '19 at 22:23

3 Answers3

7

You may create a template context processor, first create a python file with a method that returns a dictionary with the info you need then add the route to this file in your project settings, using your example:

create a file context_processors.py on your app:

 from myapp.models import MyModel

 def my_context(request):
    context_data = dict()
    context_data['notification_count'] = MyModel.objects.all().count()
    return context_data

The add the context processor in your settings.py

TEMPLATE_CONTEXT_PROCESSORS = (
    #  ...
    'myapp.context_proccessors.my_context',
)

The you can use the 'variable' in any template:

<p>Count: {{ notification_count }}</p>
jacksboo
  • 94
  • 5
  • Its not always return 2. I need read it from database then return row count as number. Also must return row sooner or later. Can template context do it? – Gereltod Mar 20 '15 at 05:15
  • You can import the model in your context processors file and change the number 2 by anything ;) – jacksboo Mar 20 '15 at 05:17
  • 4
    Since Django 1.8 TEMPLATE_CONTEXT_PROCESSORS is deprecated. Actual add 'myapp.context_proccessors.my_contex' into the 'context_processors' option in the OPTIONS of a DjangoTemplates backend instead. See docs: https://docs.djangoproject.com/en/1.8/ref/settings/#template-context-processors – Stan Zeez Nov 25 '16 at 20:25
3

You can define a context processor function and add it to TEMPLATE_CONTEXT_PROCESSORS list defined in settings.py.

Let's assume you have a file named project/context_processors.py, you can create a function that basically passes your value to all templates.

def notification_count(request):
    return {'notification_count': 2}

settings.py

TEMPLATE_CONTEXT_PROCESSORS = (
    ...
    'context_proccessors.notification_count',
)

Calling {{notification_count}} in your template will return 2.

You can take a look at the official documentation for more info about context processors.


Second approach would be, as you said, using middleware. However, I think that would be an overkill since you can do much more complicated calculations via middlewares.

middlewares.py

class NotificationsMiddleware(object):
    def process_template_response(self, request, response):
        if not ('notification_count' in response.context_data):
            response.context_data['notification_count'] = 2
        return response

Please note that, you should add the middleware into MIDDLEWARE_CLASSES list defined in your settings file.

MIDDLEWARE_CLASSES += ('middlewares.NotificationsMiddleware',)

Please see the middleware documentation for more information on how to alter request-response cycle in Django framework.

Hope this helps.

Ozgur Vatansever
  • 49,246
  • 17
  • 84
  • 119
  • 1. I followed your context_processor method. But there is error: Put 'django.contrib.auth.context_processors.auth' in your TEMPLATE_CONTEXT_PROCESSORS setting in order to use the admin application. – Gereltod Mar 20 '15 at 05:14
  • If there is already a `TEMPLATE_CONTEXT_PROCESSORS` list defined in your settings, add your context processor to it instead of the one imported from `django.conf.global_settings`. In other words, delete this line `from django.conf.global_settings import TEMPLATE_CONTEXT_PROCESSORS` and try again. I've updated my answer. – Ozgur Vatansever Mar 20 '15 at 05:16
  • Well done for showing an example with Middleware as the OP asked. – ViaTech May 16 '19 at 22:25
3

Middleware allows you to bind data to the request, meaning if you use Middleware and not a Context Processor, you can access this universal variable data in both templates and views, or actually anywhere where a request object is used throughout your codebase.

In my mind, this makes Middleware superior to Context Processors if you actually need a universal variable that is available everywhere. In short, and according to the docs: https://docs.djangoproject.com/en/2.2/topics/http/middleware/ Middleware is called on every view call.

Here is a simple example using your code and Middleware

class NotificationsMiddleware(object):

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):

        # I am showing this as a standard var not a dict as you originally had it, 
        # for simplicity, this is now a request attribute called notification_count
        request.notification_count = 2

        response = self.get_response(request)

        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        # You don't need this method for the middleware to work anymore
        # with Django as the __call__ method covers it, so you can delete it.
        pass

Now pass the Custom Middleware to settings like so:

MIDDLEWARE = [
    ...
    # custom middleware
    '[YOUR-PATH].NotificationsMiddleware'
]

Now you can call the variable in any template like so:

{{ request.notification_count }}

Or in any view, like so:

def index(request):

    print(' Notification Count =', request.notification_count)
    ...
    return render(request, 'index.html', { })

ViaTech
  • 2,143
  • 1
  • 16
  • 51