快速找到想要的文章是用户比较常规的需求,所以文章搜索也是一个博客必要的功能。
修改视图
实现搜索的功能,我们继续在article_list
的视图中添加功能。
这里我们查询的时候一般的需求是需要输入某个字符串然后在标题和文章内容中都去匹配。
这时候我就需要使用到Django中的Q对象了。
当我们在查询的条件中需要组合条件时(例如两个条件“且”或者“或”)时。我们可以使用Q()查询对象。
我们可以使用符号&或者|将多个Q()对象组合起来传递给filter(),exclude(),get()等函数。当多个Q()对象组合起来时,Django会自动生成一个新的Q()。例如下面代码就将两个条件组合成了一个
Q(question__startswith='Who') | Q(question__startswith='What')
使用上述代码可以使用SQL语句这么理解:
WHEREquestionLIKE 'Who%' ORquestionLIKE 'What%'
接着我们来对article_list()
进行改造
def article_list(request):
# 根据GET请求中查询条件
# 返回不同排序的对象数组
search = request.GET.get('search')
order = request.GET.get('order')
# 用户搜索逻辑
if search:
if order == 'total_views':
# 用 Q对象 进行联合搜索
article_list = Article.objects.filter(
Q(title__icontains=search) |
Q(body__icontains=search)
).order_by('-total_views')
else:
article_list = Article.objects.filter(
Q(title__icontains=search) |
Q(body__icontains=search)
)
else:
# 将 search 参数重置为空
search = ''
if order == 'total_views':
article_list = Article.objects.all().order_by('-total_views')
else:
article_list = Article.objects.all()
paginator = Paginator(article_list, 2)
page = request.GET.get('page')
articles = paginator.get_page(page)
# 修改此行
context = {'articles': articles, 'order': order, 'search': search }
return render(request, 'article/list.html', context)
主要修订如下:
- 新增参数search,存放需要搜索的文本。若search不为空,则检索特定文章对象。
- Q(title__icontains=search)意思是在模型的title字段查询,icontains是不区分大小写的包含,中间用两个下划线隔开。search是需要查询的文本。多个Q对象用管道符|隔开,就达到了联合查询的目的。
修改模板
接着改造list模板,要修改的代码比较多,实际上功能上没多大改变,直接贴list.html 的完整代码如下:
templates/article/list.html
<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %}
{% load static %}
<!-- 写入 base.html 中定义的 title -->
{% block title %}
首页
{% endblock title %}
<!-- 写入 base.html 中定义的 content -->
{% block content %}
{#<div class="row">#}
<!-- 定义放置文章标题的div容器 -->
<br>
<div class="container">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="{% url 'list' %}?search={{ search }}">
最新
</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'list' %}?order=total_views&search={{ search }}">
最热
</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 mb-2 mr-sm-2"
name="search"
placeholder="搜索文章..."
required
>
</form>
</div>
</div>
<!-- 新增,搜索提示语 -->
{% if search %}
{% if articles %}
<h4><span style="color: red">"{{ search }}"</span>的搜索结果如下:</h4>
<hr>
{% else %}
<h4>暂无<span style="color: red">"{{ search }}"</span>有关的文章。</h4>
<hr>
{% endif %}
{% endif %}
{% for article in articles %}
<div class="row mt-2">
<!-- 文章内容 -->
<div class="col-sm-12">
<!-- 卡片容器 -->
<div class="card h-100">
<!-- 标题 -->
<!-- <h4 class="card-header">{{ article.title }}</h4>-->
<!-- 摘要 -->
<div class="card-body">
<h4 class="card-title">{{ article.title }}</h4>
<br>
<p class="card-text">{{ article.body|slice:'100' }}...</p>
<a href="{% url 'detail' article.id %}" class="card-link">阅读本文</a>
<!-- 这里增加阅读量和图标 -->
<small class="col align-self-end" style="color: gray;">
<span class="bi bi-eye">
{{ article.total_views }}
</span>
</small>
</div>
</div>
</div>
</div>
{% endfor %}
<br>
<!-- 页码导航 -->
<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>
</div>
{#</div>#}
{% endblock content %}
重点改动如下:
- 面包屑组件、页码组件都改动了href:增加了search参数
- 新增搜索栏,以GET请求提交search参数;required属性阻止用户提交空白文本
- 新增搜索提示语。好的UI必须让用户了解当前的状态
完成后测试效果如下:
结语
截止到本篇,我们已经完成了博客文章,评论和用户管理的核心功能,同时也实现了简单的排序和搜索等必要的小功能模块。
本系列博文的核心模块将告一段落,下篇我们来完成最终的挑战:部署上线。