文章详情页
一、文章详情页访问设计
访问文章详情页,访问文章路径类似:https://www.cnblogs.com/wupeiqi/articles/3148888.html
参照访问路径编写文章详情页路由如下:
1 2 3 4 5 | urlpatterns = [ ... # 文章详情页 re_path( '^(?P<username>\w+)/articles/(?P<article_id>\d+)$' , views.article_detail), ] |
二、文章详情页的数据构建
文章详情页的head部分和左侧区域应该和个人站点保持一致。因此需要用到继承extend。
1、创建并编写base.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | <! DOCTYPE html> < html lang="en"> < head > < meta charset="UTF-8"> < title >Title</ title > <!-- 引入 Bootstrap 核心 CSS 文件 --> < link rel="stylesheet" href="/static/blog/bootstrap-3.3.7/css/bootstrap.css"> <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) --> < script src="/static/js/jquery-3.3.1.js"></ script > <!-- 引入 Bootstrap 核心 JavaScript 文件 --> < script src="/static/blog/bootstrap-3.3.7/js/bootstrap.js"></ script > <!--依赖jquery--> < style > * { margin: 0; padding: 0; } .header { width: 100%; height: 60px; background-color: #369; } .header .title { font-size: 18px; /* 字体大小 */ font-weight: 100; /* 字体粗细 */ line-height: 60px; /* 行高与页头一致,完成居中 */ color: white; margin-left: 15px; margin-top: -10px; } .backend { float: right; /* 浮动到右边 */ color: white; text-decoration: none; /* 去除下划线 */ font-size: 16px; margin-right: 12px; margin-top: 10px; } .pub_info { margin-top: 10px; color: darkgray; } </ style > </ head > < body > < div class="header"> < div class="content"> <!--站点标题--> < p class="title"> < span >{{ blog.title }}</ span > < a href="" class="backend">管理</ a > </ p > </ div > </ div > < div class="container"> < div class="row"> < div class="col-md-3"> <!--添加bootstrap面板--> < div class="panel panel-warning"> < div class="panel-heading">我的标签</ div > < div class="panel-body"> {% for tag in tag_list %} < p >< a href="/{{ username }}/tag/{{ tag.0 }}">{{ tag.0 }}({{ tag.1 }})</ a ></ p > {% endfor %} </ div > </ div > < div class="panel panel-danger"> < div class="panel-heading">随笔分类</ div > < div class="panel-body"> {% for cate in cate_list %} < p >< a href="/{{ username }}/category/{{ cate.0 }}">{{ cate.0 }}({{ cate.1 }})</ a ></ p > {% endfor %} </ div > </ div > < div class="panel panel-success"> < div class="panel-heading">随笔归档</ div > < div class="panel-body"> {% for date in date_list %} < p >< a href="/{{ username }}/archive/{{ date.0 }}">{{ date.0 }}({{ date.1 }})</ a ></ p > {% endfor %} </ div > </ div > </ div > < div class="col-md-9"> {% block content %} {% endblock %} </ div > </ div > </ div > </ body > </ html > |
注意:base.html保留了原先home_site.html大部分代码,将col-md-9的部分替换为如下:
1 2 3 4 5 | < div class="col-md-9"> {% block content %} {% endblock %} </ div > |
block
告诉模版引擎: 子模版可能会覆盖掉模版中的这些位置。
2、修改home_site.html继承base.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | {% extends 'base.html' %} {% block content %} < div class="article_list"> < div class="article_list"> {% for article in article_list %} < div class="article-item clearfix"> < h5 >< a href="">{{ article.title }}</ a ></ h5 > < div class="article-desc"> {# 文章摘要 #} {{ article.desc }} </ div > <!--文章下方详细信息--> < div class="small pub_info pull-right"> {# 文章发布时间 #} < span >发布于 {{ article.create_time|date:"Y-m-d H:i" }}</ span > {# 评论数 #} < span class="glyphicon glyphicon-comment"></ span > 评论({{ article.comment_count }}) {# 点赞数 #} < span class="glyphicon glyphicon-thumbs-up"></ span > 点赞({{ article.up_count }}) </ div > </ div > < hr > {% endfor %} </ div > </ div > {% endblock %} |
extends
标签是这里的关键。它告诉模版引擎,这个模版“继承”了另一个模版。当模版系统处理这个模版时,首先将定位父模版——在此例中,就是“base.html”。
模版引擎将注意到 base.html
中的 block
标签,并用子模版中的内容来替换这些block。
3、详情页面head部分和左侧区域数据引入
(1)方法一
由于home_site.html和article_detail.html都需要引入如下数据:
1 2 3 4 5 6 7 8 9 10 11 | user = UserInfo.objects. filter (username = username).first() blog = user.blog cate_list = models.Category.objects. filter (blog = blog).values( "pk" ).annotate(c = Count( "article__title" ))\ .values_list( "title" , "c" ) tag_list = models.Tag.objects. filter (blog = blog).values( "pk" ).annotate(c = Count( "article" ))\ .values_list( "title" , "c" ) date_list = models.Article.objects. filter (user = user).extra(select = { "y_m_date" : "date_format(create_time, '%%Y-%%m')" })\ .values( "y_m_date" ).annotate(c = Count( "nid" )).values_list( "y_m_date" , "c" ) |
在views.py中定义一个新的函数get_classification_data:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def get_classification_data(username): user = UserInfo.objects. filter (username = username).first() blog = user.blog cate_list = models.Category.objects. filter (blog = blog).values( "pk" ).annotate(c = Count( "article__title" )) \ .values_list( "title" , "c" ) tag_list = models.Tag.objects. filter (blog = blog).values( "pk" ).annotate(c = Count( "article" )) \ .values_list( "title" , "c" ) date_list = models.Article.objects. filter (user = user).extra( select = { "y_m_date" : "date_format(create_time, '%%Y-%%m')" }) \ .values( "y_m_date" ).annotate(c = Count( "nid" )).values_list( "y_m_date" , "c" ) return { "blog" : blog, "cate_list" : cate_list, "tag_list" : tag_list, "date_list" : date_list} |
依此可以改写视图函数home_site和article_detail,以article_detail为例:
1 2 3 4 5 | def article_detail(request, username, article_id): context = get_classification_data(username) return render(request, "article_detail.html" , context) |
方法二:运用inclusion_tag
在cnblog/blog/目录下创建templatestags目录,再创建my_tags.py文件。
(1)先快速创建简单tag,简单tag:只带一个参数,返回经过处理的字符串
Django提供了一种simple_tag方法来快速创建类似这样的tag。
1 2 3 4 5 6 7 8 | from django import template register = template.Library() # 乘法的函数 @register .simple_tag def multi_tag(x,y): return x * y |
修改article_detail.html,load用来载入一个过滤器或者tag, 在这里引入my_tags:
1 2 3 4 5 6 7 | {% extends "base.html" %} {% block content %} {% load my_tags %} {% multi_tag 3 9 %} {% endblock %} |
显示效果如下所示:
(2)创建inclusion tag
样式和数据结合为一个整体的时候可以考虑使用inclusion_tag.
my_tags.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | from django import template from django.db.models import Count from blog import models register = template.Library() # 乘法的函数 @register .simple_tag def multi_tag(x,y): return x * y @register .inclusion_tag( "classification.html" ) def get_classification_style(username): user = models.UserInfo.objects. filter (username = username).first() blog = user.blog cate_list = models.Category.objects. filter (blog = blog).values( "pk" ).annotate(c = Count( "article__title" )) \ .values_list( "title" , "c" ) tag_list = models.Tag.objects. filter (blog = blog).values( "pk" ).annotate(c = Count( "article" )) \ .values_list( "title" , "c" ) date_list = models.Article.objects. filter (user = user).extra( select = { "y_m_date" : "date_format(create_time, '%%Y-%%m')" }) \ .values( "y_m_date" ).annotate(c = Count( "nid" )).values_list( "y_m_date" , "c" ) return { "username" : username, "blog" : blog, "cate_list" : cate_list, "tag_list" : tag_list, "date_list" : date_list} |
将base.html中左侧边栏的面板代码内容取出,放置于classification.html中:
base.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <body> <div class = "header" > <div class = "content" > <! - - 站点标题 - - > <p class = "title" > <span>{{ blog.title }}< / span> <a href = " " class=" backend">管理< / a> < / p> < / div> < / div> <div class = "container" > <div class = "row" > <div class = "col-md-3" > <! - - 添加bootstrap面板 - - > { % load my_tags % } { % get_classification_style username % } < / div> <div class = "col-md-9" > { % block content % } { % endblock % } < / div> < / div> < / div> < / body> |
classification.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <div> <div class = "panel panel-warning" > <div class = "panel-heading" >我的标签< / div> <div class = "panel-body" > { % for tag in tag_list % } <p><a href = "/{{ username }}/tag/{{ tag.0 }}" >{{ tag. 0 }}({{ tag. 1 }})< / a>< / p> { % endfor % } < / div> < / div> <div class = "panel panel-danger" > <div class = "panel-heading" >随笔分类< / div> <div class = "panel-body" > { % for cate in cate_list % } <p><a href = "/{{ username }}/category/{{ cate.0 }}" >{{ cate. 0 }}({{ cate. 1 }})< / a>< / p> { % endfor % } < / div> < / div> <div class = "panel panel-success" > <div class = "panel-heading" >随笔归档< / div> <div class = "panel-body" > { % for date in date_list % } <p><a href = "/{{ username }}/archive/{{ date.0 }}" >{{ date. 0 }}({{ date. 1 }})< / a>< / p> { % endfor % } < / div> < / div> < / div> |
修改article_detail视图函数:
1 2 3 4 5 6 | def article_detail(request, username, article_id): # context = get_classification_data(username) # return render(request, "article_detail.html", context) return render(request, "article_detail.html" , locals ()) |
三、文章详情页渲染的标签字符串转义
Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。
1、article_detail视图函数中应拿到文章对象传入模板中
1 2 3 4 5 6 | def article_detail(request, username, article_id): user = UserInfo.objects. filter (username = username).first() blog = user.blog article_obj = models.Article.objects. filter (pk = article_id).first() return render(request, "article_detail.html" , locals ()) |
2、模板语法一旦检查到标签字符串都会进行转义
首先需要确认网站文章内容保存的并不是一个文本,而是一堆标签字符串。这样编辑文章时才会有各种各样的样式。
将文章内容直接传进模板中。

{% extends "base.html" %} {% block content %} <h3 class="text-center">{{ article_obj.title }}</h3> <div class="cont"> {{ article_obj.content }} </div> {% endblock %}
但是直接复制网站的html源代码进文章内容中,显示是不正确的。
如上所示:将<h1>hello</h1>存进数据库,当服务器传给浏览器的时候,却变成了<h1>hello</h>,浏览器按特殊符号渲染,因此显示为<h1>hello</h1>。
由此可见,模板语法一旦检查到标签字符串都会进行转义。
3、在Django中关闭HTML的自动转义
如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。比如:
1 2 | < p >这行代表会被自动转义</ p >: {{ data }} < p >这行代表不会被自动转义</ p >: {{ data|safe }} |
其中第二行我们关闭了Django的自动转义。
我们还可以通过{%autoescape off%}的方式关闭整段代码的自动转义,比如下面这样:
1 2 3 | {% autoescape off %} Hello {{ name }} {% endautoescape %} |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术