Alexander Pavlov
Alexander Pavlov

Reputation: 699

Add "active" navigation class to navbar in Django's class-based views

In a Django app, a lot of class-based views are called directly from the urls.py like so

url(r'^manage/thing/$',
    ListView.as_view(model=Thing,
                     queryset=Thing.objects.filter(parent=None)),
    name='mg_thing_list'),

and most CBVs are not subclassed, so views.py is almost empty. I want to highlight the active section in the navbar based on the URL. For example, pages at /manage/thing/... should have class="active" next to the "thing" item in the navbar HTML. What's the DRYest way of doing this?

I want to avoid subclassing the CBVs just to add the request to the template context (which the standard templatetag solution seems to require). Currently we do it in a dumb way: add a {% block %} for every navbar item tag and set the relevant block to class="active" in each and every template. Seems a waste.

Upvotes: 1

Views: 1510

Answers (3)

The Django Ninja
The Django Ninja

Reputation: 393

You can use this snippet to add active class to your html code. It does a reverse resolve based on the url name parameter.

In your_app/templatetags/base_utils.py

from django import template
from django.core import urlresolvers

register = template.Library()    

@register.simple_tag(takes_context=True)
def current(context, url_name, return_value=' active', **kwargs):
    matches = current_url_equals(context, url_name, **kwargs)
    return return_value if matches else ''


def current_url_equals(context, url_name, **kwargs):
    resolved = False
    try:
        resolved = urlresolvers.resolve(context.get('request').path)
    except:
        pass
    matches = resolved and resolved.url_name == url_name
    if matches and kwargs:
        for key in kwargs:
            kwarg = kwargs.get(key)
            resolved_kwarg = resolved.kwargs.get(key)

            if kwarg:
                # for the comparison of same type url arg d+ w+
                kwarg = unicode(kwarg)

            if not resolved_kwarg or kwarg != resolved_kwarg:
                return False
    return matches

In your_template.html

{% load base_utils %}
<li class="{% current 'your_url_name' param1=param1 param_N=param_N %}"><a href="{% url 'your_url_name' param1=param1 param_N=param_N %}"> This is NavBar Button </a></li>

Upvotes: 2

Mikeec3
Mikeec3

Reputation: 649

First of all, it's not recommended to hold logic in urls.py, also there are 2 ways to go about your issue here

First a template context processor: (in myapp/context_processors.py)

def mytag(request):
     context = {'class': 'active'}
     return context

Then

<div class='{{ class }}'>

Finally add the template context processor to TEMPLATE_CONTEXT_PROCESSORS in settings.py or in Django 1.8

TEMPLATES = [
{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': ['components/templates'],
    'APP_DIRS': True,
    'OPTIONS': {
        'context_processors': [
            'django.template.context_processors.debug',
            'django.template.context_processors.request',
            'django.contrib.auth.context_processors.auth',
            'django.contrib.messages.context_processors.messages',
            'myapp.context_processors.mytag',
        ],
    },
},

Also, just as a very common opinion on views, you should keep view logic in views, that way if you needed to build some mixins you could. Keep urls small.

Upvotes: 2

shellbye
shellbye

Reputation: 4848

I have had the question if I understand you correctly, and I solved it by using jQuery like this:

function mainNavListAddClass() {
    var theA = $("a[href=" + "'" + getCurrentUrlPath() + "'" + "]");
    if (theA.length) {
        theA.first().addClass("active");
    }
}

function getCurrentUrlPath() {
    return location.pathname;
}

Upvotes: 0

Related Questions