44

Short version:

Is there a simple, built-in way to identify the calling view in a Django template, without passing extra context variables?

Long (original) version:

One of my Django apps has several different views, each with its own named URL pattern, that all render the same template. There's a very small amount of template code that needs to change depending on the called view, too small to be worth the overhead of setting up separate templates for each view, so ideally I need to find a way to identify the calling view in the template.

I've tried setting up the views to pass in extra context variables (e.g. "view_name") to identify the calling view, and I've also tried using {% ifequal request.path "/some/path/" %} comparisons, but neither of these solutions seems particularly elegant. Is there a better way to identify the calling view from the template? Is there a way to access to the view's name, or the name of the URL pattern?


Update 1: Regarding the comment that this is simply a case of me misunderstanding MVC, I understand MVC, but Django's not really an MVC framework. I believe the way my app is set up is consistent with Django's take on MVC: the views describe which data is presented, and the templates describe how the data is presented. It just happens that I have a number of views that prepare different data, but that all use the same template because the data is presented the same way for all the views. I'm just looking for a simple way to identify the calling view from the template, if this exists.

Update 2: Thanks for all the answers. I think the question is being overthought -- as mentioned in my original question, I've already considered and tried all of the suggested solutions -- so I've distilled it down to a "short version" now at the top of the question. And right now it seems that if someone were to simply post "No", it'd be the most correct answer :)

Update 3: Carl Meyer posted "No" :) Thanks again, everyone.

bryan
  • 2,223
  • 1
  • 22
  • 19
  • I'm not sure if I can think of a reason why different views should be calling the same template? Can you elaborate? I think it may be a case that you've misunderstood the MVC concept – Mez Apr 27 '09 at 06:20
  • Thanks for the comment. Each of the views performs a different ORM query, but all output is in the same format, hence the common template for the sake of DRY. I've edited the question with further details. – bryan Apr 27 '09 at 06:24
  • 2
    I would go with passing an extra context variable to the template. Simple, no-brainer solution. Why not? – codeape Apr 27 '09 at 06:48
  • http://stackoverflow.com/questions/2491605/how-to-get-the-current-urlname-using-django – Ciro Santilli OurBigBook.com Jun 07 '16 at 20:16

11 Answers11

36

Since Django 1.5, the url_name is accessible using:

request.resolver_match.url_name

Before that, you can use a Middleware for that :

from django.core.urlresolvers import resolve

class ViewNameMiddleware(object):  
    def process_view(self, request, view_func, view_args, view_kwargs):
        url_name = resolve(request.path).url_name
        request.url_name = url_name

Then adding this in MIDDLEWARE_CLASSES, and in templates I have this:

{% if request.url_name == "url_name" %} ... {% endif %}

considering a RequestContext(request) is always passed to the render function. I prefer using url_name for urls, but one can use resolve().app_name and resolve().func.name, but this doesn't work with decorators - the decorator function name is returned instead.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Tisho
  • 8,320
  • 6
  • 44
  • 52
  • actually this middleware is not required as the request already provides the resolved url_name => request.resolver_match.url_name – Hedde van der Heide Oct 31 '14 at 12:21
  • 6
    Thanks for the note. Actually, `request.resolver_match` was introduced in Django 1.5 in 2013, while the post is from 2012. There should be a flag for 'Obsolete' in StackOverflow :) – Tisho Oct 31 '14 at 13:24
  • 2
    To use `request.resolver_match.url_name` in templates `django.core.context_processors.request` must be added to setting `TEMPLATE_CONTEXT_PROCESSORS`. – juliocesar Nov 22 '14 at 18:27
  • Is there a way to find the original url in case the resulting template is from another view redirected from original view. Like if xxx.com/create will redirect to xxxx.com/display. The redirect happens in the view() of /create url. How can i know in display.html whether it was redirectedfrom /create so that i can display a special message? – athultuttu Jul 20 '17 at 14:17
  • I'm getting `Invalid block tag: 'request.resolver_match'` when adding `` to a template, what am I missing? I do have `django.core.context_processors.request` in TEMPLATE_CONTEXT_PROCESSORS – Vadorequest Jun 01 '18 at 13:23
  • I was looking to include a snippet on all but two pages in my base.html template. I was able to do that easily with the `{{ request.path }}` template variable. – japhyr Apr 22 '20 at 19:35
19

No, and it would be a bad idea. To directly refer to a view function name from the template introduces overly tight coupling between the view layer and the template layer.

A much better solution here is Django's template inheritance system. Define a common parent template, with a block for the (small) area that needs to change in each view's version. Then define each view's template to extend from the parent and define that block appropriately.

Carl Meyer
  • 122,012
  • 20
  • 106
  • 116
  • Thanks, Carl. As mentioned in my original question, I wanted to avoid creating more templates as the template differences are very minor, but this method makes the most sense. – bryan Apr 27 '09 at 23:28
  • Actually, I was thinking about adding css class to content element based on view name, but you may be right regarding coupling. – x-yuri Nov 04 '16 at 19:57
6

If your naming is consistent in your urls.py and views.py, which it should be, then this will return the view name:

{{ request.resolver_match.url_name }}

Be sure to apply some context to it when you call it in the template. For example, I use it here to remove the delete button from my detail view, but in my update view the delete button will still appear!

{% if request.resolver_match.url_name != 'employee_detail' %}
Kermit
  • 4,922
  • 4
  • 42
  • 74
2

Since Django 1.5 you can access an instance of ResolverMatch through request.resolver_match.

The ResolverMatch gives you the resolved url name, namespace, etc.

Josep Anguera
  • 101
  • 2
  • 4
1

I'm working on this for a help-page system where I wanted each view to correspond to a help-page in my cms with a default page shown if no help page was defined for that view. I stumbled upon this blog where they use a template context processor and some python inspect magic to deduce the view name and populate the context with it.

Jens Alm
  • 3,027
  • 4
  • 22
  • 24
1

If you're using Class Based Views, you most likely have a view variable you can access.

You can use several methods from that to determine which view has been called or which template is being rendered.

e.g.

{% if view.template_name == 'foo.html' %}
# do something
{% else %}
# other thing
{% endif %}

Another option is to take out the piece of the template where you need something to change and make it into a snippet and then use {% include 'my_snippet.html' with button_type = 'bold' %} in your templates, sending arbitrary values to the snippet so it can determine what to show / how to style itself.

getup8
  • 6,949
  • 1
  • 27
  • 31
1

In your template, you can access the current view instance like this:

{{ view }}

Define class_name method in your view

class ExampleView(FormView):
    ...
    def class_name(self):
        return self.__class__.__name__

You can get the class name of the current view in a template like this:

{{ view.class_name }}

{% if view.class_name == "ExampleView" %} ... {% endif %}
Rufat
  • 536
  • 1
  • 8
  • 25
1

one simple solution is :

def view1(req):
   viewname = "view1"
   and pass this viewname to the template context   

def view2(req):
   viewname = "view2"
   and pass this viewname to the template context   

in template access the viewname as

{{viewname}} 

and also you can use this in comparisons.

Rama Vadakattu
  • 1,266
  • 2
  • 16
  • 24
  • 1
    Thanks for the answer, but as mentioned in the original question, I've already tried that and am looking for a better way, if it exists. – bryan Apr 27 '09 at 06:35
1

This sounds like the perfect example of a generic view that you can set up.

See the following resources:

These links should help you simplify your views and your templates accordingly.

Wladimir Palant
  • 56,865
  • 12
  • 98
  • 126
Nick Presta
  • 28,134
  • 6
  • 57
  • 76
  • Thanks, but my views are actually already extending generic views. Or are you suggesting that I write my own generic views? – bryan Apr 27 '09 at 06:59
  • For example, http://www.djangobook.com/en/2.0/chapter11/#cn41, 'extra_context' is passed to the template. This should be a simple solution to your problem. Define a function, or use built-in methods to pass along data in your 'extra context' so you can compare it within your template. – Nick Presta Apr 27 '09 at 07:24
  • 1
    Thanks, but as mentioned in my original question, I've already tried that, and yes I know it works. It just seems like there should be a better way to do it. I guess my question boils down to: is there a built-in way to access the calling view from a template. – bryan Apr 27 '09 at 07:51
0

Most generic views — if not all — inherits the ContextMixin which adds a view context variable that points to the View instance.

Antoine Pinsard
  • 33,148
  • 8
  • 67
  • 87
-1

Why not trying setting up a session cookie, then read the cookie from your template.

on your views set cookies

def view1(request):
 ...
#set cookie
 request.session["param"]="view1"

def view2(request):
  request.session["param"]="view2"


then in your ONE template check something like..

{% ifequal request.session.param "view1" %}
   ... do stuff related to view1
{% endifequal %}

{% ifequal request.session.param "view2" %}
  ... do stuff related to "view2"
{% endifequal %}

Gath

gath
  • 24,504
  • 36
  • 94
  • 124