小编前面做了个小调查,发现本公众号超过半数的读者可能还是学生,接触和使用Django还不满6个月,小编决定提笔另启动Django新手入门系列,与现有的Django基础系列并行(实际上现在Django基础系列更新到现在很多新手读起来已经感到有些难度了)。对于中级读者与技术大咖们,小编正在筹备Django源码阅读系列和DRF系列,会提供更深些的内容。本文是Django新手入门小知识系列的第一篇,介绍如何在模板中避免使用硬编码的url以及最佳处理方式。如果你是刚入门Django不久,觉得本文非常有帮助的话,请留个言哈!
什么是硬编码(Hard-coded)URL
假如我们有个应用名叫blog,它有一个文章Article模型( 包含title和slug两个字段)。我们还编写了两个视图函数index和detail,一个用于显示文章列表,一个用于显示文章详情(如下所示)。我们的目的是实现当用户访问articles/2/时,用户就可以看到第2篇文章的详情。
#blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('articles//', views.detail, name='detail'),
]
当我们在模板中使用for命令循环遍历文章列表,并给每篇文章添加链接时,我们可以按如下方式操作。在这里你可以看到我们使用了/articles/{{ article.id }}/手动构造了每篇文章对应的url,这没有错,但是一个糟糕的做法。任何通过手动方式拼接而成的url都是硬编码url,是Django建议避免的。试想将来如果你希望改变每篇文章对应的url,比如给每篇文章的url里加入slug字段方便搜索引擎搜索,而你的模板代码中又有多个地方链接了文章对象,那么你将需要改动所有href的属性,而且极有可能会漏掉1个或多个需要传递的参数。
<li><a href="/articles/{{ article.id }}/">{{ article.title }}a>li>
解决方法 - 使用{% url %}标签传递url
Django的推荐做法是使用{% url %}标签传递url,它的意思是访问detail视图函数对应的url,并向其传递article.id作为参数。注意:它可以接收多个参数。
<li><a href="{% url 'detail' article.id %}">{{ article.title }}a>li>
但上述做法还存在一个严重隐患。一个项目里通常包含多个app,假如我们还有一个app名为polls,也有一个detail视图函数,那么Django到底该访问哪个app下detail视图函数对应的url呢?要解决这个问题,我们就需要使用包含有命名空间的urls了, 这才是使用{% url %}标签传递url的正确方式。
第一步: 修改#blog/urls.py, 加入app_name = 'blog'.
#blog/urls.py
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.index, name='index'),
path('articles//', views.detail, name='detail'),
]
第二步:给{% url %}标签加入app_name, 这样Django就知道应该访问blog下detail视图函数对应的url了,并传递article.id作为参数。
<li><a href="{% url 'blog:detail' article.id %}">{{article.title}}a>li>
如果现在我们希望每篇文章对应的url还体现slug(比如articles/2/my-first-article-for-seo/,我们可以按如下操作。它的意思是访问blog app下detail视图函数对应的url,并传入article.id和article.slug作为参数。
<li><a href="{% url 'blog:detail' article.id article.slug %}">{{article.title}}a>li>
想想看这么做你会实现目的吗?当然不会。通过{% url %}标签传递参数的个数必须和视图函数对应url地址中参数的数量保持一致。本例中blog:detail对应的url地址里只有article _id这一个参数,而我们却试图向其传递两个参数,自然会出现url解析错误。
path('articles//', views.detail, name='detail'),
为了纠正这个错误,我们只需修改blog/urls.py,给blog:detail对应的url地址新增slug这个参数,如下所示:
path('articles///', views.detail, name='detail'),
放心,现在没有陷阱,也没有错误了。然而使用{% url %}标签传递url的方法一定是完美吗? 答案是否定的。你或许已经发现,我们虽然不用再手动拼接urls, 但每次希望给url传递新的参数时,我们还是需要手动改变模板代码。如果模板里需要改动的链接很多,也是很耗时费力的。
更好的方法 - 使用get_absolute_url方法
假如每篇文章对象都有unique独特的url地址,最好的方法是在模型models.py里通过get_absolute_url方法定义每个对象的绝对url地址,如下所示。这也是小编我推荐的方法。
#blog/models.py
class Article(models.Model):
def get_absolute_url(self):from django.urls import reversereturn reverse('blog:detail', args=[str(self.id), self.slug])
这样我们在模板中按如下使用即可。这样做的好处是,如果有一天你希望改变某篇文章对应的url,你模板里的代码一点都不需要改动,而只需要改变模型里的一句代码即可。
<li><a href="{{ article.get_absolute_url }}">{{ article.title }}a>li>
小结
Django项目中应尽量避免手动拼接而形成的硬编码url
使用{% url %}标签传递url时,别忘了使用包含有命名空间的urls
使用{% url %}标签传递的参数个数必须和对应视图函数url可接收的参数数量一致
如果一个对象有独特的url地址,使用get_absolute_url是首选方法
读完本文你的第一感受是什么?
大江狗
2020.4.3
相关阅读
Django基础(28): 如何设计充满陷阱的优美URL
Django常见错误总结: 细数我们一起走过的大坑
Django常见错误信息汇总及解决方案
如果不想错过我们的最新原创文章,关注我们的微信公众号并加星标哦。