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 %}
About us: Isotoma is a bespoke software development company based in York and London specialising in web apps, mobile apps and product design. If you’d like to know more you can review our work or get in touch.
There’s a way to determine the view name without inspecting the stack by using a bit of middleware and a context processor:
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