Search code examples
djangodjango-templatesdjango-querysetdjango-context

How to supply a QuerySet to an included template?


I have a simple Django application for a magazine website, with a sidebar that shows links for recent issues. The sidebar is its own template, which I then include into other pages. I want the Queryset for recent issues to be part of the sidebar inclusion, rather the in the view of the page that the sidebar appears on. (This is both to avoid repetition and, more importantly, to make the sidebar functional on the flatpages, which don't have views.)

This seems like iti should be a simple/common thing to do, but I can't find a good way to do it. I looked into custom template tags, context managers, etc., but all the options I saw want to return a string or a dictionary, rather than a queryset. So I ended up doing an inclusion template tag (code below), and basically building a dictionary for the issues I want displayed. It works fine, but it seems really clunky and brittle to me, and it mixes logic and display pretty badly.

Is there a better way to do this?

<!-- app_name/templates/sidebar.html -->
...
{% load pr_extras %}
...
<ul class="nav nav-sidebar">
    <li><a href="/">Home</a></li>
    <li>Previous Issues</li>
    <li>{% show_recent %}</li>
</ul>
...


# app_name/templatetags/pr_extras.py

from django import template
from mag.models import Issue

register = template.Library()

@register.inclusion_tag('recent.html')
def show_recent():
    issues = Issue.objects.order_by('-issue_num')[1:6]
    iss_dict = {}
    for i, iss in enumerate(issues):
        k = "recent_%d" % i
        iss_dict[k] = "%s %s %d" % (iss.issue_num, iss.get_season_display(), iss.year)
        u = "url_%d" % i
        iss_dict[u] = iss.issue_pdf
    return iss_dict


<!-- app_name/templates/recent.html -->
    <ul class="nav nav-sidebar">
        <li><a href="/static/uploads/{{ url_0 }}">Issue {{ recent_0 }}</a></li>
        <li><a href="/static/uploads/{{ url_1 }}">Issue {{ recent_1 }}</a></li>
        <li><a href="/static/uploads/{{ url_2 }}">Issue {{ recent_2 }}</a></li>
        <li><a href="/static/uploads/{{ url_3 }}">Issue {{ recent_3 }}</a></li>
        <li><a href="/static/uploads/{{ url_4 }}">Issue {{ recent_4 }}</a></li>
    </ul>

Solution

  • A context processor would be the typical way to do this.

    def recent_issues(request):
        {'recent_issues': return Issue.objects.order_by('-issue_num')[1:6]}
    

    Once you add the context processor to your TEMPLATES settings, you can access recent_issues in the template.

    Alternatively, if you don't want a context processor to run for every view, you can create a template tag using the simple_tag decorator (in Django < 1.9 use an assignment tag).

    @register.simple_tag
    def recent_issues():
        return Issue.objects.order_by('-issue_num')[1:6]
    

    In your template, use the tag to assign the queryset to a variable

    {% recent_issues as recent_issues %}
    

    You can now loop through recent_issues in the template.