声明:此Django分类下的教程是追梦人物所有,本人写在此只是为了巩固复习使用
首页展示的是所有文章的列表,当用户看到感兴趣的文章时,他点击文章的标题或者继续阅读的按钮,应该跳转到文章的详情页面来阅读文章的详细内容。现在让我们来开发博客的详情页面,有了前面的基础,开发流程都是一样的了:首先配置 URL,即把相关的 URL 和视图函数绑定在一起,然后实现视图函数,编写模板并让视图函数渲染模板。
设计文章详情页的 URL
回顾一下我们首页视图的 URL,在 blog\urls.py 文件里,我们写了:
blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
首页视图匹配的 URL 去掉域名后其实就是一个空的字符串。对文章详情视图而言,每篇文章对应着不同的 URL。比如我们可以把文章详情页面对应的视图设计成这个样子:当用户访问 <网站域名>/posts/1/ 时,显示的是第一篇文章的内容,而当用户访问 <网站域名>/posts/2/ 时,显示的是第二篇文章的内容,这里数字代表了第几篇文章,也就是数据库中 Post 记录的 id 值。下面依照这个规则来绑定 URL 和视图:
blog/urls.py
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.index, name='index'),
path('posts/<int:pk>/', views.detail, name='detail'),
]
这里'posts/<int:pk>/'
刚好匹配我们上面定义的 URL 规则。这条规则的含义是,以 posts/ 开头,后跟一个整数,并且以 / 符号结尾,如 posts/1/、 posts/255/ 等都是符合规则的,此外这里 <int:pk>
是 django 路由匹配规则的特殊写法,其作用是从用户访问的 URL 里把匹配到的数字捕获并作为关键字参数传给其对应的视图函数 detail
。比如当用户访问 posts/255/
时(注意 django 并不关心域名,而只关心去掉域名后的相对 URL),<int:pk>
匹配 255,那么这个 255 会在调用视图函数 detail 时被传递进去,其参数名就是冒号后面指定的名字 pk,实际上视图函数的调用就是这个样子:detail(request, pk=255)
。我们这里必须从 URL 里捕获文章的 id,因为只有这样我们才能知道用户访问的究竟是哪篇文章。
Tip:
django 的路由匹配规则有很多类型,除了这里的 int 整数类型,还有 str 字符类型、uuid 等,可以通过官方文档了解:Path converters
此外我们通过app_name='blog'
告诉 django 这个 urls.py 模块是属于 blog 应用的,这种技术叫做视图函数命名空间。我们看到 blog/urls.py 目前有两个视图函数,并且通过 name 属性给这些视图函数取了个别名,分别是 index、detail。但是一个复杂的 django 项目可能不止这些视图函数,例如一些第三方应用中也可能有叫 index、detail 的视图函数,那么怎么把它们区分开来,防止冲突呢?方法就是通过 app_name 来指定命名空间,命名空间具体如何使用将在下面介绍。如果你忘了在 blog/urls.py 中添加这一句,接下来你可能会得到一个 NoMatchReversed
异常。
为了方便地生成上述的 URL,我们在 Post 类里定义一个 get_absolute_url
方法,注意 Post 本身是一个 Python 类,在类中我们是可以定义任何方法的。
blog/models.py
from django.contrib.auth.models import User
from django.db import models
from django.urls import reverse
from django.utils import timezone
class Post(models.Model):
...
def __str__(self):
return self.title
# 自定义 get_absolute_url 方法
# 记得从 django.urls 中导入 reverse 函数
def get_absolute_url(self):
return reverse('blog:detail', kwargs={
'pk': self.pk})
注意到 URL 配置中的path('posts/<int:pk>/', views.detail, name='detail')
,我们设定的 name=‘detail’ 在这里派上了用场。看到这个 reverse 函数,它的第一个参数的值是'blog:detail'
,意思是 blog 应用下的 name=detail
的函数,由于我们在上面通过 app_name = 'blog'
告诉了 django 这个 URL 模块是属于 blog 应用的,因此 django 能够顺利地找到 blog 应用下 name 为 detail 的视图函数(虽然到此为止还没有创建detail视图),于是 reverse 函数会去解析这个视图函数对应的 URL,我们这里 detail 对应的规则就是 posts/<int:pk>/
int 部分会被后面传入的参数 pk 替换,所以,如果 Post 的 id(或者 pk,这里 pk 和 id 是等价的) 是 255 的话,那么 get_absolute_url
函数返回的就是 /posts/255/ ,这样 Post 自己就生成了自己的 URL。
编写 detail 视图函数
blog/views.py
from django.shortcuts import render, get_object_or_404
from