Search code examples
djangodjango-modelsdjango-templatesdjango-viewsdjango-tagging

Django - Global navigation in base.html using model


I have a number of templates that extend base.html. I want the base.html template to house my global navigation and have the text and links in the global navigation be based on a model Division (i.e. the CharField in the model will be used as the button text in the global nav, and the id will be used to build the URL). I thought tags might work, but what I end up with is this (yes, I'm new to Django and Python):

current_tags.py

from django import template
# Import your model
from libs.display.models import Division
from django.db import models

register = template.Library()
@register.simple_tag
def do_get_divisions(self):
    d = Division.objects.all()
    mylist = []
    for each in d:
        mylist.append(str(each.DivisionValue))
    return my list

I'm attempting just getting the text value in each object to print at this point because I can't get or work with the objects in the template, as you'll see below.

base.html

<!DOCTYPE html>
<html>
<head>
    {% load staticfiles %}
    <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}style.css" />
</head>
<body>
{% load current_tags %}
<p>{% do_get_divisions "" %}</p> **THIS ACTUALLY PRINTS mylist**

{% for each in do_get_divisions %} **THIS DOESN'T PRINT ANYTHING**
    <p>{{ each }}</p>
{% endfor %}  
{% block content %}
{% endblock %}
</body>
</html>

I'm sure there is a better way to do global nav based on a model in Django. I basically want to get all the Division objects and put them into a <ul> to use as my global nav in base.html. I am not that familiar with Django, but my views.py don't help me because I am rendering other templates, not base.html, which are extending base.html. For what it's worth, here's one views.py, where /display/info.html template extends base.html:

# Create your views here.
from django.http import HttpResponse
from apps.pulldata.models import Data
from django.shortcuts import render, get_object_or_404
from django.http import Http404

def info(request, group_id):
    group = get_object_or_404(Data, pk=group_id)
    s = group.XInGroup.all()
    return render(request, 'display/info.html', {'Group': group, 's': s})

Solution

  • You cannot put a templatetag into another. Your for-loop is a templatetag that expects the name of an element in your context to iterate over.

    If you want to handle the navigation in a template tag you should consider using inclusion tags.

    Inclusion tags are functions that use templates to render their data.

    A very basic implementation could look something like this:

    tags.py

    @register.inclusion_tag('navigation.html')
    def navigation(selected_id=None):
        return {
            'navigation': Division.objects.all(),
            'selected':selected_id,
        }
    

    In your templatetag file you create a dictionary with the navigation items and optionally the currentl selected item to highlight this navigation element.

    navigation.html

    <ul>
    {% for item in navigation %}
      <li{% if item.id == selected %} class="selected"{% endif %}>
        <a href="{{ item.get_absolute_url }}">{{ item.DivisionValue }}</a>
      </li>
    {% endfor %}
    </ul>
    

    the navigation.html uses the dictionary from the python function as context so you start with simply iterating over the navigation.

    base.html

    {% navigation %}
    

    or

    {% navigation current_division.id %}
    

    In the base.html you call the inclusion tag like a normal template tag. if you want to highlight the current item you add its id as a argument.