Django个人博客搭建1-创建Django项目和第一个App
Django个人博客搭建2-编写文章Model模型,View视图
Django个人博客搭建3-创建superuser并向数据库中添加数据并改写视图
Django个人博客搭建4-配置使用 Bootstrap 4 改写模板文件
Django个人博客搭建5-编写文章详情页面并支持markdown语法
Django个人博客搭建6-对文章进行增删查改
Django个人博客搭建7-对用户登陆注册等需求的实现
Django个人博客搭建8-优化文章模块
Django个人博客搭建9-增加文章评论模块
1.文章分页
利用Django内置的分页模块:Paginator类(:Paginator官网例子),因为是对文章分页,因此需要修改article/views.py中article_list视图:
from django.core.paginator import Paginator
# 文章列表函数
def article_list(request):
# 取出所有博客文章
article_list = ArticlePost.objects.all()
# 每页显示一篇文章
paginator = Paginator(article_list, 1)
# 获取url中的页码
page = request.GET.get('page')
# 将导航对象相应的页码内容返回给articles
articles = paginator.get_page(page)
# 需要传递给模板对象
context = {'articles': articles}
# render函数载入模板,并返回context对象
return render(request, 'article/list.html', context)
# article/list.html:模板位置 context:传入模板的对象
接下来修改templates/article/list.html加入分页内容
...
<div class="pagination row">
<div class="m-auto">
<span class="step-links">
<!-- 如果不是第一页,则显示上翻按钮 -->
{% if articles.has_previous %}
<a href="?page=1" class="btn btn-success">
« 1
</a>
<span>...</span>
<a href="?page={{ articles.previous_page_number }}"
class="btn btn-secondary"
>
{{ articles.previous_page_number }}
</a>
{% endif %}
<!-- 当前页面 -->
<span class="current btn btn-danger btn-lg">
{{ articles.number }}
</span>
<!-- 如果不是最末页,则显示下翻按钮 -->
{% if articles.has_next %}
<a href="?page={{ articles.next_page_number }}"
class="btn btn-secondary"
>
{{ articles.next_page_number }}
</a>
<span>...</span>
<a href="?page={{ articles.paginator.num_pages }}"
class="btn btn-success"
>
{{ articles.paginator.num_pages }} »
</a>
{% endif %}
</span>
</div>
</div>
最后查看效果:
分页功能完成
2.文章浏览量
首先修改文章的模型article/models.py
# 博客文章数据模型
class ArticlePost(models.Model):
...
updated = models.DateTimeField(auto_now=True)
total_views = models.PositiveIntegerField(default=0)
...
PositiveIntegerField是用于存储正整数的字段
接着生成迁移
python manage.py makemigrations
F:\Desktop\myblog>python manage.py makemigrations
System check identified some issues:
WARNINGS:
article.ArticlePost.created: (fields.W161) Fixed default value provided.
HINT: It seems you set a fixed date / time / datetime value as default for this field. This may not be what you want. If you want to have the current date as default, use `django.utils.timezone.now`
Migrations for 'article':
article\migrations\0003_auto_20190210_1956.py
- Add field total_views to articlepost
- Alter field created on articlepost
Migrations for 'userprofile':
userprofile\migrations\0002_auto_20190210_1956.py
- Alter field avatar on profile
执行迁移:
F:\Desktop\myblog>python manage.py migrate
System check identified some issues:
WARNINGS:
article.ArticlePost.created: (fields.W161) Fixed default value provided.
HINT: It seems you set a fixed date / time / datetime value as default for this field. This may not be what you want. If you want to have the current date as default, use `django.utils.timezone.now`
Operations to perform:
Apply all migrations: admin, article, auth, contenttypes, sessions, userprofile
Running migrations:
Applying article.0003_auto_20190210_1956... OK
Applying userprofile.0002_auto_20190210_1956... OK
F:\Desktop\myblog>
我们一般需要在文章列表和文章详情中显示各个文章的浏览量,因此先修改article/list.html
...
<div class="card-footer">
<a href="{% url 'article:article_detail' article.id %}" class="btn btn-primary">阅读本文</a>
<span>
<small class="col align-self-end" style="color: gray;">
浏览量:{{ article.total_views }}
</small>
</span>
...
接着修改详情模板article/detail.html:
<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %}
{% load staticfiles %}
<!-- 写入 base.html 中定义的 title -->
{% block title %}
文章详情
{% endblock title %}
<!-- 写入 base.html 中定义的 content -->
{% block content %}
<!-- 文章详情 -->
<div class="container">
...
{% if user == article.author %}
.<a href="#" onclick="confirm_delete()">删除文章</a>
.<a href="{% url "article:article_update" article.id %}">编辑文章</a>
{% endif %}
</div>
<div>
浏览量:{{ article.total_views }}
</div>
<div class="col-12">
<p>{{ article.body|safe }}</p>
...
{% endblock content %}
首先修改得就是检查登陆用户是否为文章作者本人,如果是本人才会显示删除和编辑文章,接着在下面增加显示浏览量
重启服务器:
可以看见文章列表可以显示文章浏览量了,点击阅读本文:
前面修改了修改删除文章得权限,因此用户没有登陆时时没有这两个选项的,我们点击登陆
登陆后就显示了,但是由于没有对浏览量的进行逻辑处理,就是文章的浏览量总是初始给定的0,因此修改article/views.py中article_detail使其点击一次文章详情total_views就+1:
def article_detail(request, id):
...
article.total_views += 1
article.save(update_fields=['total_views'])
...
update_fields=[]指定了数据库只更新total_views字段,优化执行效率。
我们重新刷新一下详情页面:
发现文章的浏览量已经自动增加了
这样文章浏览量功能就已经完成了
3.根据浏览量排序最热文章:
重写article/views.py:article_list():
# 文章列表函数
def article_list(request):
# 根据GET请求中查询条件
# 返回不同排序的对象数组
if request.GET.get('order') == 'total_views':
article_list = ArticlePost.objects.all().order_by('-total_views')
order = 'total_views'
else:
article_list = ArticlePost.objects.all()
order = 'normal'
paginator = Paginator(article_list, 3)
page = request.GET.get('page')
articles = paginator.get_page(page)
# 修改此行
context = {'articles': articles, 'order': order}
return render(request, 'article/list.html', context)
'-total_views’为反序,即文章浏览量高的在前面
接下来修改templates/article/list.html
...
{% block content %}
<!-- 定义放置文章标题的div容器 -->
<div class="container">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="{% url 'article:article_list' %}">
最新
</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'article:article_list' %}?order=total_views">
最热
</a>
</li>
</ol>
</nav>
<div class="row mt-2">
{% for article in articles %}
<!-- 文章内容 -->
...
重启服务器点击最热查看效果:
可以看见文章排序是按照文章浏览量排序的了
这样最热文章功能就完成了
4.搜索文章功能
Q对象
Model.objects.all()能够返回表中的所有对象。
对应的,Model.objects.filter(**kwargs)可以返回与给定参数匹配的部分对象。
还有Model.objects.exclude(**kwargs)返回与给定参数不匹配的对象
如果想对多个参数进行查询怎么办?比如同时查询文章标题和正文内容。这时候就需要Q对象。
首先修改article/views.py
# 文章列表函数
def article_list(request):
search = request.GET.get('search')
order = request.GET.get('order')
# 用户搜索逻辑,请求中有search,则走次逻辑
if search:
if order == 'total_views':
# 用q对象进行联合搜索
article_list = ArticlePost.objects.filter(
Q(title__contains=search)| # icontains不区分分大小写,contains区分大小写
Q(body__icontains=search)
).order_by('-total_views')
else:
article_list = ArticlePost.objects.filter(
Q(title__contains=search) |
Q(body__icontains=search)
)
# 根据GET请求中查询条件
# 返回不同排序的对象数组
else:
search = ''
if order == 'total_views':
article_list = ArticlePost.objects.all().order_by('-total_views')
else:
article_list = ArticlePost.objects.all()
paginator = Paginator(article_list, 3)
page = request.GET.get('page')
articles = paginator.get_page(page)
# 修改此行
context = {'articles': articles, 'order': order, 'search': search}
return render(request, 'article/list.html', context)
接着修改模板:article/list.html
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="{% url 'article:article_list' %}">
最新
</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'article:article_list' %}?order=total_views">
最热
</a>
</li>
</ol>
</nav>
<div class="row">
<div class="col-auto mr-auto">
<form class="form-inline">
<label class="sr-only">
content
</label>
<input type="text"
class="form-control"
name="search"
placeholder="搜索文章..."
required>
</form>
</div>
</div>
{# 搜索提示语 #}
{% if search %}
<h4>
<span style="color: red">{{ search }}</span>
的搜索内容如下
</h4>
<hr>
{% else %}
<h4>暂无<span style="color: red">{{ search }}</span> </h4>
有关的文章
<hr>
{% endif %}
<div class="row mt-2">
{% for article in articles %}
<!-- 文章内容 -->
<div class="col-4 mb-4">
<!-- 卡片容器 -->
<div class="card h-100">
<!-- 标题 -->
<h4 class="card-header">{{ article.title }}</h4>
<!-- 摘要 -->
<div class="card-body">
<p class="card-text">{{ article.body|slice:'100' }}...</p>
</div>
<!-- 注脚 -->
<div class="card-footer">
<a href="{% url 'article:article_detail' article.id %}" class="btn btn-primary">阅读本文</a>
<span>
<small class="col align-self-end" style="color: gray;">
浏览量:{{ article.total_views }}
</small>
</span>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<div class="pagination row">
<div class="m-auto">
<span class="step-links">
<!-- 如果不是第一页,则显示上翻按钮 -->
{% if articles.has_previous %}
<a href="?page=1&order={{ order }}&search={{ search }}" class="btn btn-success">
« 1
</a>
<span>...</span>
<a href="?page={{ articles.previous_page_number }}&order={{ order }}&search={{ search }}"
class="btn btn-secondary"
>
{{ articles.previous_page_number }}
</a>
{% endif %}
<!-- 当前页面 -->
<span class="current btn btn-danger btn-lg">
{{ articles.number }}
</span>
<!-- 如果不是最末页,则显示下翻按钮 -->
{% if articles.has_next %}
<a href="?page={{ articles.next_page_number }}&order={{ order }}&search={{ search }}"
class="btn btn-secondary"
>
{{ articles.next_page_number }}
</a>
<span>...</span>
<a href="?page={{ articles.paginator.num_pages }}&order={{ order }}&search={{ search }}"
class="btn btn-success"
>
{{ articles.paginator.num_pages }} »
</a>
{% endif %}
</span>
</div>
</div>
面包屑组件、页码组件都改动了href:增加了search参数
新增搜索栏,以GET请求提交search参数;required属性阻止用户提交空白文本
新增搜索提示语。好的UI必须让用户了解当前的状态
我们重启服务器打开文章列表页面
搜索java
这样文章的搜索功能就完成了
5.渲染Markdown目录
首先修改article/views.py:
# 文章详情
def article_detail(request, id):
...
article.body = markdown.markdown(article.body,
extensions=[
# 包含 缩写、表格等常用扩展
'markdown.extensions.extra',
# 语法高亮扩展
'markdown.extensions.codehilite',
# 目录扩展
'markdown.extensions.toc',
])
...
添加了一行代码,接着我们在之前的文章里添加几个级别的标题
其实就是插入不同级别的标题,然后在文章任何地方插入**[TOC]**即可自动生成目录
点击完成后观察效果:
如果我们想实现将目录插入到页面任何一个位置就徐娅萍修改Markdown的渲染方法:
# 文章详情
def article_detail(request, id):
article = ArticlePost.objects.get(id=id)
article.total_views += 1
article.save(update_fields=['total_views'])
md = markdown.Markdown(
extensions=[
# 包含 缩写、表格等常用扩展
'markdown.extensions.extra',
# 语法高亮扩展
'markdown.extensions.codehilite',
# 目录扩展
'markdown.extensions.toc',
]
)
article.body = md.convert(article.body)
context = {'article': article, 'toc': md.toc}
# 载入模板,并返回context对象p
return render(request, 'article/detail.html', context)
为了能将toc单独提取出来,我们先将Markdown类赋值给一个临时变量md,然后用convert()方法将正文渲染为html页面。通过md.toc将目录传递给模板。
修改article/detail.html
...
<!-- 文章详情 -->
<div class="container">
<div class="row">
<div class="col-9">
<h1 class="col-12 mt-4">{{ article.title }}</h1>
<div class="col-12 alert alert-success">作者: {{ article.author }}
{% if user == article.author %}
.<a href="#" onclick="confirm_delete()">删除文章</a>
.<a href="{% url "article:article_update" article.id %}">编辑文章</a>
{% endif %}
</div>
<div>
浏览量:{{ article.total_views }}
</div>
<div class="col-12">
<p>{{ article.body|safe }}</p>
</div>
</div>
{# 目录 #}
<div class="col-3 mt-4">
<h4>
<strong>目录</strong>
<hr>
<div>
{{ toc|safe }}
</div>
</h4>
</div>
</div>
</div>
<script>
//删除文章的函数
function confirm_delete() {
...
将原来的内容装进col-9的容器中,将右侧col-3的空间留给目录,toc需要|safe标签才能正确渲染
我们重新刷新页面:
发现右边已经正确显示目录了
Django个人博客搭建1-创建Django项目和第一个App
Django个人博客搭建2-编写文章Model模型,View视图
Django个人博客搭建3-创建superuser并向数据库中添加数据并改写视图
Django个人博客搭建4-配置使用 Bootstrap 4 改写模板文件
Django个人博客搭建5-编写文章详情页面并支持markdown语法
Django个人博客搭建6-对文章进行增删查改
Django个人博客搭建7-对用户登陆注册等需求的实现
Django个人博客搭建8-优化文章模块
Django个人博客搭建9-增加文章评论模块