Why on earth do I have to pass RequestContext in all of my responses?
Asked Answered
F

3

5

I want to highlight the current page in the navigation menu. Obviously I need to give the menu links a class like 'active' when you are on their page. This is a classic problem and I've seen many solutions proposed. My problem is I hate all of them and consider none of them to be very DRY. For example:

@register.simple_tag
def active(request, pattern):
    import re
    if re.search(pattern, request.path):
        return 'active'
    return ''

----

{% load tags %}
<div id="navigation">
    <a class="{% active request "^/about/" %}" href="/about/">About</a>
    <a class="{% active request "^/contact/" %}" href="/contact/">Contact</a>
    <a class="{% active request "^/services/" %}" href="/services/">Services</a>
</div>

The tag takes your current request and a url expression and returns 'active' if you're currently on this page. Alternatively this can be done with named views rather than urls but the principle is the same.

My main issue with this is that my navigation will be called on 99% of my views and yet, in order to get the current request variable I still have parse a RequestContext to the template with something like this:

def contact(request):
    # snip ...
    return render_to_response(
                'contact.html',
                { 'myvar' : myvar },
                context_instance=RequestContext(request))

Why do I need to add this context_instance line to every single one of my views when probably all but one of them needs the request variable in order to get the current url/view to highlight the active link? This seems awfully wet, especially for a feature that must be in the great majority of django sites. I want the request to be included by default and be able to optionally suppress it. I can't find a way to do this in middleware as I can't intercept the template before its rendered after the view has returned it.

Any suggestions?

Furiya answered 27/6, 2009 at 7:37 Comment(0)
L
16

Your intention makes sense, you'll need RequestContext most of the time and only rarely it can be safely omitted for performance reasons. The solution is simple, instead of render_to_response use direct_to_template shortcut:

from django.views.generic.simple import direct_to_template

def contact(request):
    # snip ...
    return direct_to_template(request, 'contact.html', { 'myvar' : myvar })

... or render_to decorator from django-annoying:

from annoying.decorators import render_to

@render_to('template.html')
def foo(request):          
    bar = Bar.object.all()  
    return {'bar': bar}     
Laywoman answered 27/6, 2009 at 14:23 Comment(3)
The @render_to decorator is brilliant! Thanks!Furiya
+1 for recommending @render_to! note that django-annoying has been taken over & moved due to inactivityHalonna
4 years later, and Django 1.5 has removed the 'direct_to_template'. What would now be the recommended way for doing this, without using django-annoyingBradski
I
1

You don't necessarily have to do anything to the markup of your navigation to give the current one a different style - there are declarative ways to do that using CSS.

See my answer here: Django: Is there a better way to bold the current page link for an example.

Inquisition answered 27/6, 2009 at 7:42 Comment(2)
Naming your pages with body id's is a good idea, but the body tag, along with the navigation, is in a base template. I could block it out and add {% block body_id %}my-page{% endblock %} to each extending template but again, where's the DRY? Is there any way to automate this? I would want to either use the named view as the body id or slugify it or something, rather than having to explicitly name it and have to maintain two separate 'names' for each view: one for the view itself and one for the css id.Furiya
My Django-fu is rusty, but rather than changing each extending template, could you pass in the CSS name from contact() and family: def contact(request): render_to_response('contact.html', {'cssClass': 'contact-page', 'myvar': myvar}, ...) ? Still not perfectly DRY, but not bad.Inquisition
L
0

For future reference, one can use django-tabs for doing what OP wanted.

Lookthrough answered 10/2, 2011 at 12:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.