zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Django 博客开发教程 8 - 博客文章详情页

django博客教程开发 文章 详情页
2023-09-11 14:17:33 时间

首页展示的是所有文章的列表,当用户看到感兴趣的文章时,他点击文章的标题或者继续阅读的按钮,应该跳转到文章的详情页面来阅读文章的详细内容。现在让我们来开发博客的详情页面,有了前面的基础,开发流程都是一样的了:首先配置 URL,即把相关的 URL 和视图函数绑定在一起,然后实现视图函数,编写模板并让视图函数渲染模板。

设计文章详情页的 URL

回顾一下我们首页视图的 URL,在 blogurls.py 文件里,我们写了:

blog/urls.py

from django.conf.urls import url

from . import views

urlpatterns = [

 url(r^$, views.index, name=index),

]

首页视图匹配的 URL 去掉域名后其实就是一个空的字符串。对文章详情视图而言,每篇文章对应着不同的 URL。比如我们可以把文章详情页面对应的视图设计成这个样子:当用户访问 网站域名 /post/1/ 时,显示的是第一篇文章的内容,而当用户访问 网站域名 /post/2/ 时,显示的是第二篇文章的内容,这里数字代表了第几篇文章,也就是数据库中 Post 记录的 id 值。下面依照这个规则来绑定 URL 和视图:

blog/urls.py

from django.conf.urls import url

from . import views

app_name = blog

urlpatterns = [

 url(r^$, views.index, name=index),

 url(r^post/(?P pk [0-9]+)/$, views.detail, name=detail),

]

Django 使用正则表达式来匹配用户访问的网址。这里 r^post/(?P pk [0-9]+)/$ 整个正则表达式刚好匹配我们上面定义的 URL 规则。这条正则表达式的含义是,以 post/ 开头,后跟一个至少一位数的数字,并且以 / 符号结尾,如 post/1/、 post/255/ 等都是符合规则的,[0-9]+ 表示一位或者多位数。此外这里 (?P pk [0-9]+) 表示命名捕获组,其作用是从用户访问的 URL 里把括号内匹配的字符串捕获并作为关键字参数传给其对应的视图函数 detail。比如当用户访问 post/255/ 时(注意 Django 并不关心域名,而只关心去掉域名后的相对 URL),被括起来的部分 (?P pk [0-9]+) 匹配 255,那么这个 255 会在调用视图函数 detail 时被传递进去,实际上视图函数的调用就是这个样子:detail(request, pk=255)。我们这里必须从 URL 里捕获文章的 id,因为只有这样我们才能知道用户访问的究竟是哪篇文章。

可能上述的正则表达式你有点难以理解,关于正则表达式的部分并非 Django 相关的内容,而是 Python 的内容。Django 只是在这里使用了 Python 处理正则表达式的 re 模块。因此如果想更好地理解 Python 中正则表达式的相关知识,请自行查看 Python 官方文档中 re 模块的文档。

此外我们通过 app_name=blog 告诉 Django 这个 urls.py 模块是属于 blog 应用的,这种技术叫做视图函数命名空间。我们看到 blogurls.py 目前有两个视图函数,并且通过 name 属性给这些视图函数取了个别名,分别是 index、detail。但是一个复杂的 Django 项目可能不止这些视图函数,例如一些第三方应用中也可能有叫 index、detail 的视图函数,那么怎么把它们区分开来,防止冲突呢?方法就是通过 app_name 来指定命名空间,命名空间具体如何使用将在下面介绍。如果你忘了在 blogurls.py 中添加这一句,接下来你可能会得到一个 NoMatchReversed 异常。

为了方便地生成上述的 URL,我们在 Post 类里定义一个 get_absolute_url 方法,注意 Post 本身是一个 Python 类,在类中我们是可以定义任何方法的。

blog/models.py

from django.db import models

from django.contrib.auth.models import User

from django.urls import reverse

from django.utils.six import python_2_unicode_compatible

@python_2_unicode_compatible

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 配置中的 url(r^post/(?P pk [0-9]+)/$, views.detail, name=detail) ,我们设定的 name=detail 在这里派上了用场。看到这个 reverse 函数,它的第一个参数的值是 blog:detail,意思是 blog 应用下的 name=detail 的函数,由于我们在上面通过 app_name = blog 告诉了 Django 这个 URL 模块是属于 blog 应用的,因此 Django 能够顺利地找到 blog 应用下 name 为 detail 的视图函数,于是 reverse 函数会去解析这个视图函数对应的 URL,我们这里 detail 对应的规则就是 post/(?P pk [0-9]+)/ 这个正则表达式,而正则表达式部分会被后面传入的参数 pk 替换,所以,如果 Post 的 id(或者 pk,这里 pk 和 id 是等价的) 是 255 的话,那么 get_absolute_url 函数返回的就是 /post/255/ ,这样 Post 自己就生成了自己的 URL。

编写 detail 视图函数

接下来就是实现我们的 detail 视图函数了:

blog/views.py

from django.shortcuts import render, get_object_or_404

from .models import Post

def index(request):

 # ...

def detail(request, pk):

 post = get_object_or_404(Post, pk=pk)

 return render(request, blog/detail.html, context={post: post})

视图函数很简单,它根据我们从 URL 捕获的文章 id(也就是 pk,这里 pk 和 id 是等价的)获取数据库中文章 id 为该值的记录,然后传递给模板。注意这里我们用到了从 django.shortcuts 模块导入的 get_object_or_404 方法,其作用就是当传入的 pk 对应的 Post 在数据库存在时,就返回对应的 post,如果不存在,就给用户返回一个 404 错误,表明用户请求的文章不存在。

编写详情页模板

接下来就是书写模板文件,从下载的博客模板(如果你还没有下载,请 点击这里 下载)中把 single.html 拷贝到 templatesblog 目录下(和 index.html 在同一级目录),然后改名为 detail.html。此时你的目录结构应该像这个样子:

blogproject\

 manage.py

 blogproject\

 __init__.py

 settings.py

 blog/

 __init__.py

 models.py

 templates\

 blog\

 index.html

 detail.html

在 index 页面博客文章列表的标题和继续阅读按钮写上超链接跳转的链接,即文章 post 对应的详情页的 URL,让用户点击后可以跳转到 detail 页面:

templates/blog/index.html

 article 

 header 

 a href="{{ post.get_absolute_url }}" {{ post.title }} /a 

 /h1 

 /header 

 div 

 div 

 a href="{{ post.get_absolute_url }}" 继续阅读 span → /span /a 

 /div 

 /div 

 /article 

{% empty %}

 div 暂时还没有发布的文章! /div 

{% endfor %}

这里我们修改两个地方,第一个是文章标题处:

 h1 

 a href="{{ post.get_absolute_url }}" {{ post.title }} /a 

 /h1 

我们把 a 标签的 href 属性的值改成了 {{ post.get_absolute_url }}。回顾一下模板变量的用法,由于 get_absolute_url 这个方法(我们定义在 Post 类中的)返回的是 post 对应的 URL,因此这里 {{ post.get_absolute_url }} 最终会被替换成该 post 自身的 URL。

同样,第二处修改的是继续阅读按钮的链接:

 a href="{{ post.get_absolute_url }}" 继续阅读 span → /span 

 /a 

这样当我们点击首页文章的标题或者继续阅读按钮后就会跳转到该篇文章对应的详情页面了。然而如果你尝试跳转到详情页后,你会发现样式是乱的。这在 真正的 Django 博客首页 时讲过,由于我们是直接复制的模板,还没有正确地处理静态文件。我们可以按照介绍过的方法修改静态文件的引入路径,但很快你会发现在任何页面都是需要引入这些静态文件,如果每个页面都要修改会很麻烦,而且代码都是重复的。下面就介绍 Django 模板继承的方法来帮我们消除这些重复操作。

我们看到 index.html 文件和 detail.html 文件除了 main 标签包裹的部分不同外,其它地方都是相同的,我们可以把相同的部分抽取出来,放到 base.html 里。首先在 templates 目录下新建一个 base.html 文件,这时候你的项目目录应该变成了这个样子:

blogproject\

 manage.py

 blogproject\

 __init__.py

 settings.py

 blog\

 __init__.py

 models.py

 templates\

 base.html

 blog\

 index.html

 detail.html

把 index.html 的内容全部拷贝到 base.html 文件里,然后删掉 main 标签包裹的内容,替换成如下的内容。

templates/base.html

 main 

 {% block main %}

 {% endblock main %}

 /main 

 aside 

 {% block toc %}

 {% endblock toc %}

 /aside 

...

这里 block 也是一个模板标签,其作用是占位。比如这里的 {% block main %}{% endblock main %} 是一个占位框,main 是我们给这个 block 取的名字。下面我们会看到 block 标签的作用。同时我们也在 aside 标签下加了一个 {% block toc %}{% endblock toc %} 占位框,因为 detail.html 中 aside 标签下会多一个目录栏。当 {% block toc %}{% endblock toc %} 中没有任何内容时,{% block toc %}{% endblock toc %} 在模板中不会显示。但当其中有内容是,模板就会显示 block 中的内容。

在 index.html 里,我们在文件最顶部使用 {% extends base.html %} 继承 base.html,这样就把 base.html 里的代码继承了过来,另外在 {% block main %}{% endblock main %} 包裹的地方填上 index 页面应该显示的内容:

templates/blog/index.html

{% extends base.html %}

{% block main %}

 {% for post in post_list %}

 article 

 /article 

 {% empty %}

 div 暂时还没有发布的文章! /div 

 {% endfor %}

 !-- 简单分页效果

 div 

 a href="#" 上一页 /a 

 span 第 6 页 / 共 11 页 /span 

 a href="#" 下一页 /a 

 /div 

 div 

 /div 

{% endblock main %}

这样 base.html 里的代码加上 {% block main %}{% endblock main %} 里的代码就和最开始 index.html 里的代码一样了。这就是模板继承的作用,公共部分的代码放在 base.html 里,而其它页面不同的部分通过替换 {% block main %}{% endblock main %} 占位标签里的内容即可。

如果你对这种模板继承还是有点糊涂,可以把这种继承和 Python 中类的继承类比。base.html 就是父类,index.html 就是子类。index.html 继承了 base.html 中的全部内容,同时它自身还有一些内容,这些内容就通过 “覆写” {% block main %}{% endblock main %}(把 block 看做是父类的属性)的内容添加即可。

detail 页面处理起来就简单了,同样继承 base.html ,在 {% block main %}{% endblock main %} 里填充 detail.html 页面应该显示的内容,以及在 {% block toc %}{% endblock toc %} 中填写 base.html 中没有的目录部分的内容。不过目前的目录只是占位数据,我们在以后会实现如何从文章中自动摘取目录。

templates/blog/detail.html

{% extends base.html %}

{% block main %}

 article 

 /article 

 section 

 /section 

{% endblock main %}

{% block toc %}

 div 

 h3 文章目录 /h3 

 a href="#" 教程特点 /a 

 /li 

 a href="#" 谁适合这个教程 /a 

 /li 

 a href="#" 在线预览 /a 

 /li 

 a href="#" 资源列表 /a 

 /li 

 a href="#" 获取帮助 /a 

 /li 

 /ul 

 /div 

{% endblock toc %}

修改 article 标签下的一些内容,让其显示文章的实际数据:

 article 

 header 

 h1 {{ post.title }} /h1 

 div 

 span a href="#" {{ post.category.name }} /a /span 

 span a href="#" time datetime="{{ post.created_time }}" {{ post.created_time }} /time /a /span 

 span a href="#" {{ post.author }} /a /span 

 span a href="#" 4 评论 /a /span 

 span a href="#" 588 阅读 /a /span 

 /div 

 /header 

 div 

 {{ post.body }}

 /div 

 /article 

再次从首页点击一篇文章的标题或者继续阅读按钮跳转到详情页面,可以看到预期效果了!

博客文章详情页

本章节的代码位于:Step8: blog detail view

如果遇到问题,请通过下面的方式寻求帮助。

博客文章详情页- 追梦人物的博客 的评论区留言。 将问题的详细描述通过邮件发送到 djangostudyteam@163.com,一般会在 24 小时内回复。

更多Django 教程,请访问 追梦人物的博客


Django博客上云及“飞天加速”计划参与体验 搭建Django博客并部署到阿里云ECS服务器上,使用Gunicorn+Nginx+Supervisor进行管理,部署过程中遇到了许多问题,如服务器虚拟环境创建失败、端口配置失败等等,通过查找资料一一解决,也感谢阿里云提供的体验机会。
开启 Django 博客的 RSS 功能 文中所涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 博客提供 RSS 订阅应该是标配,这样读者就可以通过一些聚合阅读工具订阅你的博客,时时查看是否有文章更新,而不必每次都跳转到博客上来查看。现在我们就来为博客添加 RSS 订阅功能。 RSS 简介 RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,它采用 XML 作为内容传递的格式。简单来说就是网站可以把内容包装成符合 RSS 标准的 XML 格式文档。一旦网站内容符合一个统一的规范,那么人们就可以开发一种读取这种规范化的 XML 文档的工具来聚合各大网站的内容。例如一个读者
HelloDjango 系列教程:创建 Django 博客的数据库模型 HelloDjango 系列教程:创建 Django 博客的数据库模型文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 设计博客的数据库表结构博客最主要的功能就是展示我们写的文章,它需要从某个地方获取博客文章数据才能把文章展示出来,通常来说这个地方就是数据库。
7月12日,晴,陆陆续续的把Django入门教程看完了,了解到了一些前端知识,数据库,python的一些语法,比较顺利的开发了一个究极简陋的个人博客,实现了文章的发布与修改,也算是对Django有了初步的认识,激励我继续完成个人博客开发! 1.熟悉settings.py文件的架构 2.掌握Model的运用 3.掌握urls的设置 4.编写Templates,通过浏览器呈现内容 5.熟悉Admin管理工具的运用 通过整一个流程下来,可以完成一个能上传文章,修改文章的简易博客,但之前一直是跟着教程打代码,好多细节都记不清了,甚至忘了。