Django:如何从模板中识别调用视图?

44

简版:

在Django模板中,有没有一种简单的内置方法来识别调用视图,而不需要传递额外的上下文变量?

长(原始)版:

我的一个Django应用程序有几个不同的视图,每个视图都有自己的命名URL模式,并且所有这些视图都呈现相同的模板。有很少量的模板代码需要根据调用的视图进行更改,但太小了,无法为每个视图设置单独的模板。因此,理想情况下,我需要找到一种方法在模板中识别调用的视图。

我尝试设置视图以传递额外的上下文变量(例如“view_name”)来标识调用的视图,我还尝试使用{% ifequal request.path "/some/path/" %}比较,但是这些解决方案都不是特别优雅。是否有更好的方法来从模板中识别调用视图?是否有一种方法可以访问视图的名称或URL模式的名称?


更新1:关于评论说这只是我误解了MVC的情况,我理解MVC,但是 Django并不是真正的MVC框架 。我认为我的应用程序设置方式符合Django对MVC的理解:视图描述呈现哪些数据,模板描述如何呈现数据。正好我有许多描述不同数据的视图,但是所有视图都使用相同的模板,因为数据在所有视图中以相同的方式呈现。我只是想找到一种简单的方法来从模板中识别调用的视图,如果存在这样的方法的话。

更新2: 感谢所有的回答。我认为这个问题被过度思考了--正如我在原始问题中提到的那样,我已经考虑和尝试了所有建议的解决方案--因此,我已经简化了问题,现在置于问题的顶部的是“简短版本”。目前似乎,如果有人只是发表“不”,那么它将是最正确的答案:)

更新3: Carl Meyer 发布了 "不" :) 再次感谢大家。


我不确定为什么不同的视图应该调用相同的模板?你能详细说明一下吗?我认为这可能是因为你误解了MVC的概念。 - Mez
感谢您的评论。每个视图执行不同的ORM查询,但所有输出都以相同的格式呈现,因此为了DRY而使用了通用模板。我已经编辑了问题并提供了更多细节。 - bryan
2
我会选择向模板传递一个额外的上下文变量。这是一个简单而明显的解决方案。为什么不呢? - codeape
https://dev59.com/g3E95IYBdhLWcg3wCJRf - Ciro Santilli OurBigBook.com
11个回答

36

自从Django 1.5版本以后,可以使用以下方式访问url_name

request.resolver_match.url_name

在那之前,你可以使用中间件来实现:

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

然后在 MIDDLEWARE_CLASSES 中添加这个,然后在模板中我有这个:

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

考虑到在渲染函数中始终传递RequestContext(request)。我更喜欢使用url_name来处理URL,但可以使用resolve().app_name和resolve().func.name,但这对装饰器无效 - 返回的是装饰器函数名。


实际上,这个中间件是不必要的,因为请求已经提供了解析后的url_name => request.resolver_match.url_name - Hedde van der Heide
6
谢谢留言。实际上,request.resolver_match是在2013年的Django 1.5中引入的,而这篇文章是在2012年发布的。在StackOverflow上应该有一个“已过时”的标志 :) - Tisho
2
要在模板中使用 request.resolver_match.url_name,必须将 django.core.context_processors.request 添加到设置 TEMPLATE_CONTEXT_PROCESSORS 中。 - juliocesar
有没有办法在结果模板来自原始视图的重定向情况下找到原始URL。例如,如果xxx.com/create将重定向到xxxx.com/display,则重定向发生在/create URL的view()中。我如何知道在display.html中是否从/create重定向,以便我可以显示特殊消息? - athultuttu
当我在模板中添加<body class="{% request.resolver_match.url_name %}">时,出现了Invalid block tag: 'request.resolver_match'的错误提示,我错过了什么吗?我已经在TEMPLATE_CONTEXT_PROCESSORS中添加了django.core.context_processors.request - Vadorequest
我想在我的base.html模板中包含除两个页面外的所有页面上的代码片段。使用{{ request.path }}模板变量,我很容易做到了这一点。 - japhyr

19

不可以这样做,且这是一个糟糕的主意。在模板中直接引用视图函数名称会导致视图层和模板层之间过于紧密地耦合。

更好的解决方案是使用Django的模板继承系统。首先定义一个通用的父模板,其中包含每个视图版本中需要更改的区域(较小)。然后定义每个视图的模板以从父模板继承,并适当地定义该块。


谢谢,卡尔。正如我在原问题中提到的,我想避免创建更多的模板,因为模板之间的差异非常小,但这种方法是最合理的。 - bryan
其实,我在考虑根据视图名称向内容元素添加CSS类,但是你可能对于耦合性是正确的。 - x-yuri

6
如果您在 urls.py 和 views.py 中的命名一致(应该是这样的),那么这将返回视图名称:
{{ request.resolver_match.url_name }}

在模板中调用时,请确保对其应用一些上下文。例如,我在此处使用它从我的详细视图中删除删除按钮,但在我的更新视图中删除按钮仍将出现!

{% if request.resolver_match.url_name != 'employee_detail' %}

2

谢谢,我一直在寻找view_name的标识! - tbolender

1
在您的模板中,您可以像这样访问当前视图实例:
{{ view }}

在你的视图中定义class_name方法。
class ExampleView(FormView):
    ...
    def class_name(self):
        return self.__class__.__name__

你可以在模板中像这样获取当前视图的类名:
{{ view.class_name }}

{% if view.class_name == "ExampleView" %} ... {% endif %}

1

一个简单的解决方案是:

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   

在模板中访问视图名称:

{{viewname}} 

同时你也可以在比较中使用它。


1
谢谢您的回答,但正如原问题中所提到的,我已经尝试过了并正在寻找更好的方法(如果存在)。 - bryan

1

我正在为一个帮助页面系统工作,在这个系统中,我希望每个视图都对应着我的 CMS 中的一个帮助页面,并且如果没有为该视图定义帮助页面,则显示默认页面。我偶然发现了这篇博客,他们使用了一个模板上下文处理器和一些 Python 检查魔法来推断视图名称并在上下文中填充它。


1

谢谢,但我的观点已经扩展了通用视图。或者您是在建议我编写自己的通用视图? - bryan
1
谢谢,但正如我原来的问题所提到的那样,我已经尝试过了,是的,我知道它可以工作。只是似乎应该有更好的方法来做这件事。我想我的问题归结为:是否有一种内置的方法可以从模板中访问调用视图。 - bryan

1
如果您正在使用基于类的视图,则很可能可以访问view变量。您可以使用该变量中的几种方法来确定调用了哪个视图或正在呈现哪个模板。例如:
{% if view.template_name == 'foo.html' %}
# do something
{% else %}
# other thing
{% endif %}

另一个选项是取出模板中需要更改的部分并将其制作成代码片段,然后在模板中使用{% include 'my_snippet.html' with button_type = 'bold' %},向代码片段发送任意值,以便确定应显示/如何设置样式。

0

大多数通用视图——如果不是全部——都继承了ContextMixin,它添加了一个view上下文变量,指向视图实例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接