An important part of a usable web site is a well-designed navigation bar. Something that any navigation bar should have is an indication of where the user is. Common practices for this include using a bold face for the link, a different color or adding a small icon next to the current section link. To do that, you could include your navigation code in every page and use an “active” class name on the a element to distinguish it from the other links.
<div id="navigation">
<a href="/">Home</a>
<a href="/services/">Services</a>
<a class="active" href="/contact/">Contact</a>
</div>
Writing this in every template quickly becomes a pain in the neck. With Django, an easy solution would be to move this section to a different file, _nav.html for instance, and include it in every page. Another, even better way, would be to make sure that every template extends a base template (a very common practice with Django) and to add the navigation to that base template.
We face a little problem however: how do we know which page we’re on? If you use the render_to_response shortcut, you can give an extra keyword argument, context_instance. If you use the RequestContext class, you can pass more data to the template (the generic views use RequestContext by default, so you don’t have to worry about them.) Here’s how to do it:
## settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
)
## views.py
from django.template import ContextRequest
def home(request):
return render_to_request('home.html', {}, context_instance=RequestContext(request))
You can now use the request template variable in home.html and its parent templates, which should include your base template. The HttpRequest object has an attribute, path, which contains the current URL (starting after the host name). We can now add the active class to the link for the current section:
<div id="navigation">
<a class="{% ifequal request.path "/" %}active{% endifequal %}" href="/">Home</a>
<a class="{% ifequal request.path "/services/" %}active{% endifequal %}" href="/services/">Services</a>
<a class="{% ifequal request.path "/contact/" %}active{% endifequal %}" href="/contact/">Contact</a>
</div>
This code has two problems: (1) it’s really ugly (it’s probably all that should matter to make you want to rework this code), and (2) imagine that there are subsections under the services section, /services/web-hosting/, /services/tech-support/, etc. When you’re under one of those subsections, the link to the services section will not be highlighted. We’ll fix that by using a custom template tag that will check if request.path begins with a given string.
## tags.py
@register.simple_tag
def active(request, pattern):
if request.path.startswith(pattern):
return 'active'
return ''
<!-- navigation -->
{% load tags %}
<div id="navigation">
<a class="{% active request "/" %}" href="/">Home</a>
<a class="{% active request "/services/" %}" href="/services/">Services</a>
<a class="{% active request "/contact/" %}" href="/contact/">Contact</a>
</div>
Ah, the code is much nicer and now if we go to /services/web-hosting/, the services link will still be highlighted. Another link will be highlighted unfortunately: the home link will ALWAYS be highlighted, because every path begins with a slash. Let’s change the implementation of the active tag slightly to use regular expressions and we’ll also change the calls to {% active %}
## tags.py
@register.simple_tag
def active(request, pattern):
import re
if re.search(pattern, request.path):
return 'active'
return ''
<!-- navigation -->
{% load tags %}
<div id="navigation">
<a class="{% active request "^/$" %}" href="/">Home</a>
<a class="{% active request "^/services/" %}" href="/services/">Services</a>
<a class="{% active request "^/contact/" %}" href="/contact/">Contact</a>
</div>
Nice! Now it’s how we want it. Home will be highlighted only when we’re viewing the / URL, the subsections still work and the code is still pretty cute.
Thats a neat solution. I just worked around the same problem just this morning by writing a custom middleware (to intercept process_view), a context processor func and a decorator to decorate every view func. Your solution is a whole lot simpler and elegant.
Very neat. Uncannily similar to a solution I came up with for Rails. (I’m familiar with Rails but plan to learn Django too).
Intermediate templates and inheritance are what you want; then you don’t need any tags at all :)
James: I don’t see how that would help out. I still need to be aware of the page I’m currently on. Hence the need for a tag.
Hello,
I just found your post, here is my solution:
http://www.martin-geber.com/weblog/2007/10/25/breadcrumbs-django-templates/
(By the way, I that’s what James means.)
Cheers.
[...] Highlight active menu option: use {{ request.path }} to know requested URL and compare it with menu options * [more info] [...]
A fix:
## views.py
from django.template import RequestContext
[...] new and improved {% active %} tag A few months ago, I described a Django template tag that I wrote to identify the currently active [...]
This made some code go way in templates! Thanks
This is very nice for my main navigation.
But what about the Side navigation that keeps track of active liknks like:
/events/1
/events/2
where each links is active depending on the current event beign displayed.
I can’t really do {% active request “/events/” + event.id %} here :-(
drozzy: Just do {% active request “/events/\d+” %} :)
Thanks, this is an intuitive and insightful intro to some fundamental functionality.
Thanks for the write-up. I was looking all over for something like this and couldn’t find it in the docs.
Thanks for an excellent and simple example!
My tags.py file ended up looking like this:
from django import template
register = template.Library()
@register.simple_tag
def active(request, pattern):
import re
if re.search(pattern, request.path):
return ‘active’
return ”
but with indentation obviously…
Thanks, this was almost exactly what I was planning on writing. You not only you saved me the time but also made a more flexible version by using regular expressions.
Can you tell me, where do you put the code? Does it belong in the view.py file? If it belongs in its own file, where does that file go?
Thank you very much.
[...] Vincent Foley has a very good article in his blog of how to implement a navigation bar in django. I include the latest paragraph of his post here, so if you are not familiar with url patterns and django template tags please read the full article of Vincent: [...]
This is a good implementation. The only problem I see is that the url pattern is coupled with the template page. To decouple it you can use url names in the urls.py and variables in the template page. I have a post on my blog on how to do the decoupling, it based on your article obviously.
110j.wordpress.com/2009/01/25/django-template-tag-for-active-class
This is a great solution. I haven’t seen anything similar posted anywhere else.
Some argue that this is a job for base_* and some CSS but I disagree. What happens when you are using a mix of normal views and flatpages? regex is at the heart of this tag which makes it so much more flexible than most other solutions.
Thanks!
Just for the record, I’m using Django 1.0.2 and it seems that ‘request’ is available by default in every template, so there’s no need to do the ‘RequestContext’ thing.
That was neat. Helped me implement simple navigation.
Thanks
Thanks for a good post, but I found some ways to imporve further. It would be much better to use the reverse method to get the urls that you need and instead use the path of the methods.
#tags.py:
from django import template
from string import Template
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
register = template.Library()
@register.simple_tag
def active(request, url, title):
s = Template(‘$title‘)
import re
print reverse(url), request.path
#if re.search(reverse(url), request.path):
if reverse(url) == request.path:
active = ‘active’
else:
active = ”
return s.substitute(active=active, link=reverse(url), title=unicode(_(title)))
#in the template
{% active request “mmp.form.views.default” “Home” %}
This tag gets the right url from urls.py, so if you change it, you just have to change it in urls.py and nowhere else. The tag also uses djangos methods for translation at the same time.
Cheers!
I found this extremly useful, thanx
my variation:
from django.template import Library
from django.core.urlresolvers import reverse
import re
register = Library()
@register.simple_tag
def active(request, pattern, return_value):
import re
if re.search(pattern, request.path):
return return_value
return “
@register.simple_tag
def reverse_active(request, url, return_value):
print reverse(url), request.path
if reverse(url) == request.path:
return return_value
return “
# template
class=”top-menu-element{% reverse_active request “catalog.views.browse” ” top-menu-element-active” %}”
class=”top-menu-element{% active request “/help/” ” top-menu-element-active” %}”
I get the following error:
‘DebugParser’ object has no attribute ‘path’
Electrical appliances with, camera of Motorola?Another cannon to, include wholegrain breads.And therefore are, any weight loss.Car stocks for COULD NOT CREATE HTTPREQUEST, this encrypt all and save it.On the processes, game especially in.,
What tamplate do you use in your blog? Very interesting articles
I came up with a much simpler solution for my site.
Inheriting templates can mark the active page by defining the corresponding nav* style, e.g. .navHome {background-color: #4A91C3;} Otherwise, .navHome is not defined and the link is not marked as active.
Home
Users
Billing
Oops, my source code got rendered as HTML. Let me try again:
Home Users BillingOnce again:
<div class=”navigation”>
<a class=”navHome” href=”home”>Home</a>
<a class=”navUsers” href=”users”>Users</a>
<a class=”navBilling” href=”billing”>Billing</a>
</div>
If anyone having troubles with missing “request”, it’s not this code’s fault, it’s yours :D
Spent 10mins figuring it out:
In every request on views.py, never forget:
context_instance=RequestContext(request)
as last argument
Even in render_to_response :D
Thanks for this idea for tab switching
@Carlos “In every request …” So much for the DRY principle. I think “in every” is the keyword for boilerplate code. There must be a better solution!
Very nice solution. Simply clever. Thank you Vincent!
Excellent post thank you for the info
[...] See this article for more information: http://gnuvince.wordpress.com/2007/09/14/a-django-template-tag-for-the-current-active-page/ [...]
[...] 参考文章:http://gnuvince.wordpress.com/2007/09/14/a-django-template-tag-for-the-current-active-page/ [...]
This is a good but not generic solution, only a minimal one. There should be some middleware to keep track of the menu hierarchy and apps should be able to expose and register their (sub)menus.
[...] 29.09.2010 0 0 http://gnuvince.wordpress.com/2007/09/14/a-django-template-tag-for-the-current-active-page/ [...]
Here’s what I did that didn’t require any tags. In my base template I added classes to my menu using blocks:
class=”{% block active_aboutus %}{% endblock %}”
class=”{% block active_services %}{% endblock %}”
In my inherited template I simply set that to have a value:
{% block active_aboutus %}active{% endblock %}
It works well and I don’t have to fiddle with URLs or tags.
Just had a go at this but it appears that its not up to date with more recent versions of Django:
>>> from django.template import ContextRequest
Traceback (most recent call last):
File “”, line 1, in
ImportError: cannot import name ContextRequest
I guess I’ll go google!
Ah of course, its back to front. Should be RequestContext, as mentioned by someone else above.
newsletter templates…
A Django template tag for the current active page « Occasionally sane…
Acai Berry Review…
A Django template tag for the current active page « Occasionally sane…
UK Adult Webcams…
[...]A Django template tag for the current active page « Occasionally sane[...]…
Doesn’t work with Debug=True (atleast for me)
Brilliant snippet of code. Took care of deleting all the extra template nestings. Especially the transparant use of regex’s in the pattern is great.
Regards,
Gerard.
pirater msn…
[...]A Django template tag for the current active page « Occasionally sane[...]…
After Effects Templates Free…
[...]A Django template tag for the current active page « Occasionally sane[...]…