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.
Pingback: Common web features in Django at The Best of Marc Garcia
A fix:
## views.py
from django.template import RequestContext
Pingback: The new and improved {% active %} tag « Occasionally sane
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.
Pingback: django template tag for active CSS class « 110j
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
Pingback: emson… » Blog Archive » How To Set Up and Build a Django Web Site
Pingback: Django request.path应用,以及自定义站点全局配置信息 | 中文Python
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.
Pingback: Как получить url представления
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.
The best solution I’ve found so far: easy, quick and painless.
Thank you!
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.
Pingback: e newsletter templates
Pingback: Acai Berry Review
Pingback: UK Adult Webcams
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.
Pingback: pirater msn
Pingback: After Effects Templates Free
Pingback: Creating base.html and improving the navigation bar. | Anthony Honstain SDE
Pingback: Highlighting current active page in Django | Michał Ochman
thanks!!
Is there a way to use this to add in a specific class when a page is active?
Pingback: Get django to check current URL and if contains a particular string, to add in a class - How-To Video
My brother suggested I would possibly like this blog.
He was entirely right. This publish actually made my day.
You cann’t imagine just how a lot time I had spent for this info! Thank you!
Pingback: Wi Fi Horizon, use copoun inside
This is brilliant. Thank you.
I seriously love your blog.. Great colors & theme. Did you
build this site yourself? Please reply back as I’m planning to create my own personal blog and would like to learn where you got this from or exactly what the theme is named. Thank you!
An intriguing discussion is worth comment. I think that you should publish more about this issue, it might not be a taboo subject but typically people do not discuss these subjects. To the next! Kind regards!!
Claro y conciso. Muchas gracias. Besos!
Another problem you may have to pay then my opinion is
that we visit to any place for the purpose of roaming in that city.
But the big surprise is that depositors in Cypriot banks, a decision to introduce capital controls by Paphos Car Hire will mean at least a few times.
An NHS Berkshire East spokesperson said:” Existing patients are prescribed four tablets a day. Also set for release under the EVO View 4 G moniker on Sprint in the summer as the Himalayan Farmacia On Line without the known Farmacia On Line side effects. Asexuals have pushed to get HSDD removed from the trial.
Sleep apnea pillows offer ample neck & back assistance, even though men and women undergoing CPAP treatment make use of a mask which could cover
the nose and mouth. The special pillow helps these using the CPAP
mask by keeping the head from moving around and possibly interfering
with the action from the mask. The cpap pillow is
perfect not only since it can comfortably prop up the head but also because it may be used in tandem with any type of CPAP mask, making the treatment more comfortable and effective.