Getting the name of the current view in a Django template

Recently I have been working with generalised/reusable Django base templates, and I had the need to know the name of the current view using the template.
As it turns out, there currently aren’t any simple methods of doing this out there, and some of the nicer ones at their best make use of monkey patching.

After some playing around I found the cleanest, and quite simple, way is to use a custom template context processor.
A context processor provides extra values to bind to a template instance (it’s context; similar to including items in the data dict when calling render_to_response()).

By using a custom context processor we can assign a ‘current_view’ value to the template context, containing the namespaced name of the current view.
That just leaves one problem: how do we find the current view from within the request context.
inspect to the rescue!

Update: see Alexander Dutton’s comment for the even cleaner way by using middleware instead of stack inspection.

With inspect.stack() and inspect.getmodule() we can both grab the a stack of frames (e.g. the execution frames up to and including the current execution frame, which should be our context processor instance), and the module the frame is in.

from inspect import stack, getmodule

def ContextWithView(request):
    """Template context with current_view value,
    a string with the full namespaced django view in use.
    """
    # Frame 0 is the current frame
    # So assuming normal usage the frame of the view
    # calling this processor should be Frame 1
    name = getmodule(stack()[1][0]).__name__
    return {
        'current_view': "%s.%s" % (name, stack()[1][3]),
    }

To use this in a view, just import it and then pass it as the context in your render_to_response() call:

"""main/my_site/views/misc.py - misc views"""

from main.my_site.context_processors import ContextWithView

def my_view(request):
    # Do some stuff
    return render_to_response(
        'my_template.html',
        {},
        context_instance=ContextWithView(request)
    )

From within a template you can then use current_view, which for the above should output something like “main.my_site.views.misc.my_view”

Misc.

If you’re using RequestContext with your templates to make sure request, user etc. are always available in your templates, you can chain context processor calls like so:

from inspect import stack, getmodule
from django.template import RequestContext

def ContextWithView(request):
    """Template context with current_view value,
    a string with the full namespaced django view in use.
    """
    d = RequestContext(request)
    # Frame 0 is the current frame
    # So assuming normal usage the frame of the view
    # calling this processor should be Frame 1
    name = getmodule(stack()[1][0]).__name__
    d['current_view'] = "%s.%s" % (name, stack()[1][3])
    return d

If, like me, you then need to render a reverse URL using the ‘current_view’ variable, you’ll find the the {% url %} tag in Django only works on static strings, and not variables, so we need to make a custom tag to do this for us:

"""main/my_site/templatetags/extras.py
"""

from django.core.urlresolvers import reverse
from django import template

register = template.Library()

@register.simple_tag
def var_url(view, *args, **kwargs):
    return reverse(view, args=args, kwargs=kwargs)
{% comment %}
To use {% var_url %} in your template
just import and use as you would {% url %}
{% endcomment %}

{% load extras %}

{% block content %}
The URL for this view is <em>{% var_url current_view %}</em>.
{% endblock content %}

2 Responses to “Getting the name of the current view in a Django template”


  • There’s a way to determine the view name without inspecting the stack by using a bit of middleware and a context processor:

    class ViewNameMiddleware(object):
        def process_view(self, request, view_func, view_args, view_kwargs):
            request.view_name = ".".join((view_func.__module__, view_func.__name__))
    
    def view_name_context_processor(request):
        return {
            'view_name': request.view_name,
        }
    

    The path being handled by the current view is also available as HttpRequest.path, which can be passed in to the template using a context processor, negating the need to use reverse on the view name.

    Still, stack manipulation is always cool ;-)

  • Aha!
    This I like, far cleaner.

    The reason for not using request.path for the reverse is so I can change the params to the view, e.g. changing an ID or a step number, which to do with the path is harder than reversing on the view itself

Comments are currently closed.