现在可以为博客添加搜索功能。Django ORM可以使用contains或类似的icontains过滤器执行简单的匹配任务。比如:
from blog.models import Post Post.objects.filter(body__contains=\'framework\')然而,如果要执行更加复杂的搜索,比如通过权重或者相似性,就必须使用一个全文搜索引擎(full-text search engine)。
Django的全文检索功能基于PostgreSQL数据库的全文搜索特性,所以这个全文检索功能不能用于Django ORM支持的其他种类的数据库。PostgreSQL的全文搜索介绍在https://www.postgresql.org/docs/10/static/textsearch.html。
虽然Django ORM通过面向对象抽象,可以不依赖于具体的数据库,但是用于PostgreSQL的一部分功能无法用于其他数据库。
4.1自定义模板过滤器现在blog项目使用的是Python自带的SQLlite数据库,对于开发而言已经足够。在生产环境中,需要使用诸如MySQL,PostgreSQL和Oracle等更强力的数据库。为了实现全文搜索功能,我们将转而使用PostgreSQL。
在Linux环境下,需要先安装PostgreSQL和Python的相关依赖:
sudo apt-get install libpq-dev python-dev之后使用下列命令安装PostgreSQL:
sudo apt-get install postgresql postgresql-contrib如果使用MacOS X或者Windows,到https://www.postgresql.org/download/查看安装说明。
在安装完之后,还需要为python安装psycopg2模块:
pip install psycopg2==2.7.4译者注:使用 pip install psycopg2-binary 命令安装 psycopg2 最新版模块。
在PostgreSQL中创建一个名叫blog的用户,供项目使用。在系统命令行中输入下列命令:
su postgres createuser -dP blog会被提示输入密码。创建用户成功之后,创建一个名叫blog的数据库并将所有权设置给blog用户:
createdb -E utf8 -U blog blog译者注:PostgreSQL在Linux安装后会创建一个postgres用户,使用该用户身份可以登陆PostgreSQL数据库进行操作。
之后编辑settings.py文件中的DATABASES设置:
DATABASES = { \'default\': { \'ENGINE\': \'django.db.backends.postgresql\', \'NAME\': \'blog\', \'USER\': \'blog\', \'PASSWORD\': \'*\', } }这里我们将默认的数据库修改成了PostgreSQL,之后执行数据迁移和创建超级用户。然后可以在:8000/admin/登录管理后台。由于更换了数据库,博客应用内没有任何文章数据,录入一些数据,为之后使用全文搜索做准备。
4.2执行简单搜索编辑settings.py文件,将django.contrib.postgres加入到INSTALLED_APPS中:
INSTALLED_APPS = [ # ... \'django.contrib.postgres\', ]激活还应用后,现在可以通过search参数进行搜索:
from blog.models import Post Post.objects.filter(body__search=\'django\')这个QuerySet会使用PostgreSQL为body字段创建内容是\'django\'字符串的搜索向量和一个查询,通过匹配查询和结果向量,返回最后的结果。
4.3执行简单搜索可能想在多个字段中进行检索。在这种情况下,需要定义一个SearchVector搜索向量对象,来创建一个针对Post模型的title和body进行搜索的向量:
from django.contrib.postgres.search import SearchVector from blog.models import Post Post.objects.annotate(search=SearchVector(\'title\',\'body\'),).filter(search=\'poem\')使用分组函数然后定义了两个字段的向量,之后使用查询,就可以得到最终的结果。
全文搜索是一个密集计算过程,如果要检索的数据多于几百行,最好创建一个匹配搜索向量的索引,Django提供了一个SearchVectorField字段在模型中定义搜索向量。具体可以参考。
4.4创建搜索视图现在我们可以创建一个视图用于让用户执行搜索。首先需要一个表单让用户输入要查询的数据,编辑blog应用的forms.py增加下面的表单:
class SearchForm(forms.Form): query = forms.CharField()query字段用于输入查询内容,然后编辑blog应用的views.py文件,然后添加如下代码:
from django.contrib.postgres.search import SearchVector from .forms import EmailPostForm, CommentForm, SearchForm def post_search(request): form = SearchForm() query = None results = [] if \'query\' in request.GET: form = SearchForm(request.GET) if form.is_valid(): query = form.cleaned_data[\'query\'] results = Post.objects.annotate(search=SearchVector(\'title\', \'slug\', \'body\'), ).filter(search=query) return render(request, \'blog/post/search.html\', {\'query\': query, "form": form, \'results\': results})这个视图先初始化空白SearchForm表单,通过GET请求附加URL参数的方式提交表单。如果request.GET字典中存在query参数且通过了表单验证,就执行搜索并返回结果。
视图编写完毕,需要编写对应的模板,在/blog/post/目录下创建search.html文件,添加如下代码:
{% extends \'blog/base.html\' %} {% block title %} Search {% endblock %} {% block content %} {% if query %} <h1>Post containing {{ query }}</h1> <h3> {% with results.count as total_results %} Found {{ total_results }} result{{ total_results|pluralize }} {% endwith %} </h3> {% for post in results %} <h4> <a href="{{ post.get_absolute_url }}">{{ post.title }}</a> </h4> {{ post.body|truncatewords:5 }} {% empty %} <p>There are no results for your query.</p> {% endfor %} <p><a href="{% url \'blog:post_search\' %}">Search again</a></p> {% else %} <h1>Search for posts</h1> <form action="." method="get"> {{ form.as_p }} <input type="submit" value="Search"> </form> {% endif %} {% endblock %}