Django better way to send model data to the headers and footers

In Django, we create the templates for the headers and footers so as to mitigate the code repetition. But when, the content of the headers and footer are dynamic then for every view we need to query the content and pass the context like this:

from django.shortcuts import render
from models import HeaderModel, FooterModel

def home(request):
    #views code here:

    #get latest news    
    header_content = HeaderModel.objects.all()
    footer_content = FooterModel.objects.all()

    #info to pass into the html template
    context = {
        'header_content': header_content,
        'footer_content': footer_content,
    }

    return render(request,'home.html',context)

def login(request):
    #views code here:

    #get latest news    
    header_content = HeaderModel.objects.all()
    footer_content = FooterModel.objects.all()

    #info to pass into the html template
    context = {
        'header_content': header_content,
        'footer_content': footer_content,
    }

    return render(request,'login.html',context)

In my approach, I see that for every pages I need to repeatedly write the code for the header and footer content and pass into the context. Is there any better way to do this so that there is no repetation ??


Solution 1:

We can solve these using context processors.

Context processors are useful for building dynamic model data in templates.

We create them by writing functions. We can write as many functions as we want in a single file called context_processors.py. This file could be in a folder called context_processors. This folder could be in the general project folder. The name of the file and folder could be whatever you want. Put “init.py” file in the folder, in order to know Django this is a package. As an example, I will return in two footers types the current year (this is not a data model) and some posts (this is a data model) as contexts:

context_processors (folder)
 -> __init__.py
 -> context_processors.py

Then, register this context processor in your settings.py file, in the TEMPLATE variable:

'OPTIONS': {
   'context_processors': [
       ...
       'context_processors.context_processors.current_year_context_processor',
       'context_processors.context_processors.posts_view_context_processor',
   ],
},

Write the functions in context_processors.py file:

import datetime

from blog.models import Post


# For _footer_bottom.html template file
def current_year_context_processor(request):
    current_datetime = datetime.datetime.now()
    return {
        'current_year': current_datetime.year
    }


# For _footer.html template file
def posts_context_processor(request):
    posts = Post.objects.order_by('-updated')  # By updated date.

    return {
        'posts': posts
    }

Finally, put this in the HTML footer templates you like:

FOOTER 1 ( _footer.html ):

<ul ...>

{% if posts %}
    {% for p_record in posts|slice:":3" %}
        <li>
            <div class="footer-widget__news_image">
                <img src="{{ p_record.image_main.url }}" alt="">
            </div>
            <div class="footer-widget__news_text">
                <p><a href="{% url 'post' p_record.slug %}"> {{ p_record.title | truncatechars:45 }}</a></p>
            </div>
            <div class="footer-widget__news_date_box">
                <p>{{ p_record.updated | date:"SHORT_DATE_FORMAT" }}</p>
            </div>
        </li>
    {% endfor %}
{% else %}

    <p>No hay novedades.</p>

{% endif %}

</ul>

And in another HTML footer file:

FOOTER 2 ( _footer_bottom.html ):

Instead of using in the footer for getting the current year:

<p>@ {% now 'Y' %} Business Name. Derechos reservados.</p>

We can use a context processor, putting directly the context in the template:

<p>@ {{ current_year }} Business Name. Derechos reservados.</p>