Django by Example·第三章|Extending Your Blog Application@笔记
Django by Example·第三章|Extending Your Blog Application@笔记
之前已经写过两章内容了,继续第三章。第三章继续对博客系统的功能进行拓展,其中将会穿插一些重要的技术要点。
部分内容引用自原书,如果大家对这本书感兴趣
请支持原版Django by Example·Antonio Melé
目录
第一章:建立一个博客系统
第二章:为博客系统添加高级功能
第三章 拓展博客系统功能
上一章介绍了Django表单的基础知识、学习了如何将第三方应用程序集成到项目中。本章将涵盖以下几点:
- 创建自定义模板标签和过滤器
- 为博客添加站点地图和RSS订阅功能
- 用Solr和Haystack构建搜索引擎
1 创建自定义模板标签和过滤器
Django内置了各种模板标记,例如{% if %}或{% block %}。您在模板中使用了几个。您可以在Django模板语言使用学习到更多关于Django模板语言的相关知识.
然而,Django还允许您创建自己的模板标记来执行一些自定义操作。自定义模板标记在需要时可以非常方便的为您的模板添加Django模板未曾定义的功能。
1.1 创建自定义模板标签
Django提供了以下帮助函数,允许您以简单的方式创建自己的模板标记:
- simple_tag:处理数据并返回字符串
- inclusion_tag:处理数据并返回渲染模板
- assignment_tag:处理数据并在上下文中设置变量
模板标签必须存在于Django应用程序中。
1.1.1 使用simple_tag创建简单标签
在blog应用程序目录中,创建一个新目录,将其命名为templatetags,并向其中添加一个空__init__.py文件。在同一文件夹中创建另一个文件,将其名为blog_tags.py。此时blog应用程序的文件结构应如下所示:

templatetags下创建的python文件的文件名很重要。您将使用此名称在模板中加载这个自定义的标签。
我们将首先创建一个简单的标签,用来检索博客中发布的所有帖子。编辑刚刚创建的blog_tags.py文件,并添加以下代码:
from django import template
from ..models import Postregister = template.Library()@register.simple_tag
def total_posts():return Post.published.count()
我们创建了一个简单的模板标记,它返回到目前为止发布的帖子数量。每个模板标记模块都需要包含一个名为register的变量,这样才能将自定义的标签添加到Django的模板库里。此变量是template.Library()一个实例,用于注册您自己的模板标签和过滤器。然后我们用Python函数定义一个名为total_posts的标签,并使用@register.simple_tag将该函数定义为一个简单标签并注册。Django将使用该函数的名称作为标签名。如果您想用不同的名称注册它,可以通过指定name属性的名称来实现它,类似@register.simple_tag(name='my_tag')。
添加新模板标签模块后,您需要重新启动Django开发服务器,以便使用新模板标签和过滤器。
在使用自定义模板标签之前,必须使用Django内置的{% load %}标签使其可用于模板。如前所述,您需要使用包含模板标签和过滤器的Python模块(文件)的名称。打开blog/base.html模板并在其顶部添加{%load blog_tags%}以加载模板标签模。然后使用您创建的标签显示您的帖子总数。只需将{%total_posts%}添加到模板中。模板最终应如下所示:
{% load blog_tags %}
{% load staticfiles %}
<!DOCTYPE html>
<html>
<head><title>{% block title %}{% endblock %}</title><link href="{% static "css/blog.css" %}" rel="stylesheet">
</head><body><div id="content">{% block content %}{% endblock %}</div><div id="sidebar"><h2>My blog</h2><p>This is my blog. I've written {% total_posts %} posts so far.</p></div>
</body>
</html>
我们需要重新启动服务器以跟踪添加到项目中的新文件。使用Ctrl+C停止开发服务器,然后使用以下命令再次运行:
python manage.py runserver
打开http://127.0.0.1:8000/blog/在浏览器中。您应该在站点的侧边栏中看到帖子总数,如下所示:

自定义模板标签的强大之处在于,您可以处理任何数据并将其添加到任何模板中,而不考虑执行的视图。您可以执行QuerySet或处理任何数据以在模板中显示你想要的结果。
1.1.2 使用inclusion_tag创建一个包含标签
现在,我们将创建另一个标签,以在博客的侧边栏中显示最新的帖子。这次我们将使用包含标签。使用该标签,可以让我们通过使用模板标签返回的上下文变量来呈现模板。编辑blog_tags.py文件并添加以下代码:
@register.inclusion_tag('blog/post/latest_posts.html')
def show_latest_posts(count=5):latest_posts = Post.published.order_by('-publish')[:count]return {'latest_posts': latest_posts}
在上述代码中,我们使用@register.inclusion_tag注册模板标签,并指定必须使用blog/post/latest_posts.html模板呈现返回值。我们的模板标签将接受默认为5的可选参数,并允许我们指定要显示的帖子数。我们使用此变量限制查询Post.published.order_by(‘-publish’)[:count]的结果。注意,函数返回字典而不是简单的值。包含标签必须返回一个字典,该字典用作呈现指定模板的上下文。包含标记返回字典。我们刚刚创建的模板标记可以用于传递可选数量的注释以显示,例如{%show_latest_posts 3%}。
现在,在blog/post/下创建一个新的模板文件,并将其命名为latest_posts.html。并向其添加以下代码:
<ul>
{% for post in latest_posts %}<li><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></li>
{% endfor %}
</ul>
在这里,我们用一个无序列表来展示latest_posts标签的返回值。现在,编辑blog/base.html模板并添加新的模板标记以显示最后3条帖子。侧边栏块应如下所示:
<div id="sidebar"><h2>My blog</h2><p>This is my blog. I've written {% total_posts %} posts so far.</p><h3>Latest posts</h3>{% show_latest_posts 3 %}
</div>
模板标记被调用,传递要显示的帖子数,并在给定上下文中就地呈现相应的模板。
现在,返回浏览器并刷新页面。侧边栏现在应该如下所示:

1.1.3 使用assignment_tag创建一个赋值标签
最后,我们将创建一个赋值标签。赋值标签类似于简单标签,但它们将结果存储在给定的变量中。我们将创建一个赋值标签来显示评论最多的帖子。编辑blog_tags.py文件,并在其中添加以下代码:
rom django.db.models import Count@register.assignment_tag
def get_most_commented_posts(count=5):return Post.published.annotate(total_comments=Count('comments')).order_by('-total_comments')[:count]
此QuerySet使用annotate()函数进行查询聚合,也使用了Count聚合函数。我们在total_comments字段中构建一个QuerySet,聚合每个帖子的评论总数,并根据该字段对QuerySet进行排序。我们还提供了一个可选的计数变量来限制返回到给定值的对象总数。
除了Count之外,Django还提供了聚合函数Avg、Max、Min和Sum。有关聚合函数的详细信息,请访问Django聚合函数。
编辑blog/base.html模板并将以下代码追加到侧边栏<div>元素中:
<h3>Most commented posts</h3>
{% get_most_commented_posts as most_commented_posts %} <ul>
{% for post in most_commented_posts %}<li><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></li>
{% endfor %}
</ul>
赋值模板标签的符号为{% template_tag as variable %}。对于模板标签,我们使用{% get_most_commented_posts as most_commented_posts%}。这样,我们将模板标签返回的结果存储在名为most_commented_posts的新变量中。然后,我们就可以使用这个新变量,然后用无序列表显示返回的帖子。
现在,打开浏览器并刷新页面以查看最终结果。应该如下所示:

有关自定义模板标签的详细信息,请访问Django自定义模板标签。
1.2 自定义模板过滤器
Django有各种内置的模板过滤器,允许您修改模板中的变量。模板过滤器事实上是Python函数,它们接受一个或两个参数,这两个变量一个是你要修改的模板变量,另一个是可选参数。它们返回一个值,这个值可以直接用于模板中或者继续被其他过滤器处理。一个过滤器看起来类似于{{variable| my_filter}},或者传递一个参数{{variable| my_filter:“foo”}。您可以对{{variable|filter1|filter2}}这样的变量应用任意数量的筛选器,并且每个筛选器都将应用于上一个筛选器生成的输出。
我们将创建一个自定义过滤器,它能够将我们的帖子内容转换为Markdown格式显示在页面中。Markdown是一种简单易用的纯文本格式语法,它旨在转换为HTML。您可以学习更多有关Markdown基础知识在此处。
首先,使用以下命令通过pip安装Python markdown模块:
pip install Markdown==2.6.2
然后编辑blog_tags.py文件并包含以下代码:
from django.utils.safestring import mark_safe import markdown@register.filter(name='markdown')
def markdown_format(text):return mark_safe(markdown.markdown(text))
自定义模板过滤器和自定义标签一样,也是需要注册的。为了避免函数名和markdown模块之间的冲突,我们为自定义过滤器函数名称加一个markdown前缀,并将过滤器命名为markdown,以便在模板中使用,例如{{variable|markdown}}。Django对过滤器生成的HTML代码进行转义。我们使用Django提供的mark_safe函数将结果标记为要在模板中呈现的安全HTML。默认情况下,Django不信任任何HTML代码,并在将其放入输出之前对其进行转义。唯一的例外是标记为不可转义的变量。这种行为防止Django输出潜在危险的HTML,并允许您在知道返回安全HTML时创建异常。
现在,在文章列表和文章内容页模板中加载模板标签模块。在post/list.html和post/detail.html模板顶部的{%extends%}标记后面添加以下行:
{% load blog_tags %}
在post/detail.html模板中,使用{{ post.body|markdown }}替换以下行:
{{ post.body|linebreaks }}
然后,在post/list.html文件中,使用{{ post.body|markdown|truncatewords_html:30 }}替换以下行:
{{ post.body|truncatewords:30|linebreaks }}
truncatewords_html过滤器在一定数量的字符之后截断字符串,避免未闭合的html标记。
现在,打开http://127.0.0.1:8000/admin/blog/post/add/在浏览器中,并在正文处添加以下内容:
This is a post formatted with markdown
--------------------------------------*This is emphasized* and **this is more emphasized**.Here is a list:
* One
* Two
* ThreeAnd a [link to the Django website](https://www.djangoproject.com/)
打开浏览器,查看帖子的呈现方式。您应该看到以下内容:

如您所见,自定义模板过滤器对于自定义格式非常有用。你可以在以下位置找到有关自定义过滤器的详细信息编写自定义模板过滤器。
2 为你的网站添加站点地图
sitemap又称“网站地图”,是展示一个网站结构、栏目和内容说明等基本信息的文档,就像人们对一个陌生城市的了解需要借助于城市地图一样,对于一个网站信息的快速了解也可以借助于网站地图进行。
Django附带了一个站点地图框架,它允许您动态生成站点地图。站点地图是一个XML文件,它告诉搜索引擎网站的页面、它们的相关性以及更新的频率。通过使用站点地图,您将帮助爬虫为您的网站内容编制索引。
Django站点地图框架依赖于Django.contrib.sites,它允许您将对象关联到与项目一起运行的特定网站。当您想使用一个Django项目运行多个站点时,这很方便。要安装站点地图框架,我们需要激活项目中的站点和站点地图应用程序。编辑项目的settings.py文件,并将django.contrib.sites和django.confrib.itemaps添加到INSTALLED_APPS设置中。还要为站点ID定义一个新设置,如下所示:
SITE_ID = 1# Application definition
INSTALLED_APPS = (# ...'django.contrib.sites','django.contrib.sitemaps',
)
现在,运行以下命令在数据库中创建Django站点应用程序的表:
python manage.py migrate
站点应用程序现在已与数据库同步。现在,在blog应用程序目录中创建一个新文件,并将其命名为sitemaps.py。打开该文件并向其中添加以下代码:
from django.contrib.sitemaps import Sitemap
from .models import Postclass PostSitemap(Sitemap):changefreq = 'weekly'priority = 0.9def items(self):return Post.published.all()def lastmod(self, obj):return obj.publish
我们通过继承sitemaps模块的Sitemap类来创建自定义站点地图。changefreq和priority属性表示您的帖子展示页面的更改频率及其在网站中的相关性(最大值为1)。items()方法返回要包含在此站点地图中的对象的QuerySet。默认情况下,Django对每个对象调用get_absolute_url()方法来检索其url。请记住,我们在第1章“构建博客应用程序”中创建了这个方法,以检索帖子的规范URL。如果要为每个对象指定URL,可以向sitemap类添加位置方法。lastmod方法接收items()返回的每个对象,并返回上次修改对象的时间。changefreq和priority方法也可以是方法或属性。您可以在Django官方文档中看到完整的站点地图参考,该文档位于Django站点地图.
最后,我们只需要添加站点地图URL。编辑项目的主urls.py文件并添加如下内容:
from django.conf.urls import include, url
from django.contrib import admin
from django.contrib.sitemaps.views import sitemap
from blog.sitemaps import PostSitemapsitemaps = {'posts': PostSitemap,
)urlpatterns = [url(r'^admin/', include(admin.site.urls)),url(r'^blog/', include('blog.urls'namespace='blog', app_name='blog')),url(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
]
在这里,我们导入了所需的模块,并定义了一个站点地图字典。我们定义了一个与sitemap.xml匹配并使用sitemap视图的URL模式。站点地图字典被传递到站点地图视图。现在打开http://127.0.0.1:8000/sitemap.xml在浏览器中。您应该看到如下XML代码:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><url><loc>http://example.com/blog/2015/09/20/another-post/</loc><lastmod>2015-09-29</lastmod><changefreq>weekly</changefreq><priority>0.9</priority></url><url><loc>http://example.com/blog/2015/09/20/who-was-django-reinhardt/</loc><lastmod>2015-09-20</lastmod><changefreq>weekly</changefreq><priority>0.9</priority></url>
</urlset>
每个帖子的URL都是通过调用其get_absolute_URL()方法构建的。lastmod属性对应于我们在sitemap中指定的发布后日期字段,changefreq和priority属性也取自我们上面定义的PostSitemap类。您可以看到用于构建URL的域是example.com。该域来自数据库中存储的Site对象。这个默认对象是在我们将站点框架与数据库同步时创建的。打开http://127.0.0.1:8000/admin/sites/site/在浏览器中。您应该看到这样的内容:

这是站点框架的列表显示管理视图。在这里,您可以设置站点框架和依赖它的应用程序使用的域或主机。为了生成本地环境中存在的URL,请将域名更改为127.0.0.1:8000,如下图所示,并保存它:

在生产环境中,您必须为站点框架使用自己的域名。
3 为博客添加RSS订阅功能
Django有一个内置的syndication feed(用于管理订阅功能)框架,您可以使用它以类似于使用sitemaps框架创建站点地图的方式动态生成RSS订阅或Atom提要。
RSS是简易信息聚合“Really Simple Syndication”或“Richsite summary”(网站内容摘要)的缩写。是站点用来和其他站点之间共享内容的一种简易方式。
在blog应用程序目录中创建一个新文件,并将其命名为feeds.py。向其中添加以下代码:
from django.contrib.syndication.views import Feed
from django.template.defaultfilters import truncatewords
from .models import Postclass LatestPostsFeed(Feed):title = 'My blog'link = '/blog/'description = 'New posts of my blog.'def items(self):return Post.published.all()[:5]def item_title(self, item):return item.titledef item_description(self, item):return truncatewords(item.body, 30)
首先,我们创建一个继承syndication模块的Feed类的子类。title、link和description属性分别对应于<title>、<link>和<description>RSS元素。
items()方法检索要包含在订阅中的对象。我们仅检索此提要的最后五篇已发布文章。item_title()和item_description()方法接收items()返回的每个对象,并返回每个对象的标题和描述。我们使用truncatewords内置模板过滤器来构建包含前30个单词的博客文章描述。
现在,编辑blog应用程序的urls.py文件,导入刚刚创建的LatestPostsFeed,并为它创建新的URL模式:
from .feeds import LatestPostsFeedurlpatterns = [# ... url(r'^feed/$', LatestPostsFeed(), name='post_feed'),
]
在浏览器中打开http://127.0.0.1:8000/blog/feed/。现在您应该可以看到RSS订阅的提要,其中包括最后五篇博客文章:
<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>My blog</title><link>http://127.0.0.1:8000/blog/</link><description>New posts of my blog.</description><atom:link href="http://127.0.0.1:8000/blog/feed/" rel="self"/><language>en-us</language><lastBuildDate>Sun, 20 Sep 2015 20:40:55 -0000</lastBuildDate><item><title>Who was Django Reinhardt?</title><link>http://127.0.0.1:8000/blog/2015/09/20/who-was-django-reinhardt/</link><description>The Django web framework was named after the amazing jazz guitarist Django Reinhardt.</description><guid>http://127.0.0.1:8000/blog/2015/09/20/who-was-django-reinhardt/</guid></item>...</channel>
</rss>
如果您在RSS客户端中打开相同的URL,您将能够看到您订阅的内容。
最后一步是将提要订阅功能链接添加到博客的侧边栏。打开blog/base.html模板,在侧边栏div中的文章总数下添加以下行:
<p><a href="{% url "blog:post_feed" %}">Subscribe to my RSS feed</a></p>
现在,在浏览器中打开http://127.0.0.1:8000/blog/,查看侧边栏。您将看到RSS订阅的功能链接:

3 用Solr和Haystack构建搜索引擎
现在,我们将在博客中添加搜索功能。Django ORM允许您使用icontains过滤器执行不区分大小写的查找。例如,可以使用以下查询查找帖子正文中包含单词framework的帖子:
Post.objects.filter(body__icontains='framework')
但是,如果您需要更强大的搜索功能,则必须使用适当的搜索引擎。在这里我们将使用Solr为我们的博客构建一个搜索引擎。Solr是一个流行的开源搜索平台,它提供全文搜索、术语提升、点击突出显示、多面搜索和动态聚类等高级搜索功能。
为了将Solr集成到我们的项目中,我们将使用干Haystack。Haystack是一个Django应用程序,可以作为多个搜索引擎的抽象层。它提供了一个简单的搜索API,非常类似于Django查询集。让我们从安装和配置Solr和干堆开始。
3.1 安装Solr
您需要在您的机器上配置JAVA环境,并且是1.7版或更高版本才能安装Solr。您可以使用shell提示符中的命令java-version检查java版本。输出可能有所不同,但您需要确保安装的版本至少为1.7:
java version "1.7.0_25"
Java(TM) SE Runtime Environment (build 1.7.0_25-b15)
Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode)
如果您没有安装Java或您的版本低于所需版本,则可以从http://www.oracle.com/technetwork/java/javase/downloads/index.html下载.
检查Java版本后,从http://archive.apache.org/dist/lucene/solr/.解压缩下载的文件并转到Solr安装目录中的示例目录(即cd Solr-4.10.4/example/)。该目录包含一个可用的Solr配置。从该目录中,使用以下命令使用内置Jetty web服务器运行Solr:
java -jar start.jar
打开浏览器并输入URLhttp://127.0.0.1:8983/solr/.您应该看到以下内容:

这是Solr管理控制台。此控制台显示使用情况统计信息并允许您管理搜索后端、检查索引数据和执行查询。
3.2 创建一个Solr内核
Solr允许您隔离核心中的实例。每个Solr内核都是一个Lucene实例,包含Solr配置、数据模式和使用它所需的其他配置。Solr允许您动态创建和管理内核。此示例配置一个名为collection1的Solr内核。如果你单击core Admin菜单选项卡,您可以看到此core的信息,如下图所示:

我们将为我们的blog应用程序创建一个独立的Solr内核。首先,我们需要创建此内核的文件结构。在solr-4.10.4/目录中的示例目录中,创建一个新目录并将其命名为blog。然后在其中创建以下空文件和目录:

添加以下代码到solrconfig.xml文件:
<?xml version="1.0" encoding="utf-8" ?>
<config><luceneMatchVersion>LUCENE_36</luceneMatchVersion> <requestHandler name="/select" class="solr.StandardRequestHandler" default="true" /><requestHandler name="/update" class="solr.UpdateRequestHandler" /><requestHandler name="/admin" class="solr.admin.AdminHandlers" /><requestHandler name="/admin/ping" class="solr.PingRequestHandler"><lst name="invariants"><str name="qt">search</str><str name="q">*:*</str></st></requestHandler>
</config>
您也可以从本章附带的代码中复制此文件。这是最小的Solr配置。编辑schema.xml文件并添加以下xml代码:
<?xml version="1.0" ?>
<schema name="default" version="1.5">
</schema>
这是一个空的xml文件,稍后我们将依据此文件使用Solr的自定义模式。
现在,单击Core Admin菜单选项卡,然后单击Add Core按钮。您将看到一个类似以下的表单,允许您自定义此内核的信息:

在上述页面对应位置填写以下内容:
- name: blog
- instanceDir: blog
- dataDir: data
- config: solrconfig.xml
- schema: schema.xml
name字段是此Solr内核的名称。instanceDir字段是内核所在的的目录名称。dataDir是索引数据保存的目录,它位于instanceDir目录下。config字段是Solr XML配置文件的名称,schema字段是SolrXML数据模式文件的名称。
现在,单击Add Core按钮。如果您看到以下内容,则您的新内核已成功添加到Solr:

3.3 安装Haystack模块
要在Django中使用Solr搭建搜索引擎,我们还需要使用Haystack模块。使用以下命令通过pip安装Haystack:
pip install django-haystack==2.4.0
Haystack可以与几个搜索引擎后端交互。要使用Solr后端,还需要安装pysolr模块。运行以下命令安装它:
pip install pysolr==3.3.2
安装django-haystack和pysolr两个模块后,需要在项目中激活haystack。打开settings.py项目配置文件,将haystack添加到INSTALLED_APPS设置中,如下所示:
INSTALLED_APPS = (# ...'haystack',
)
您需要为haystack定义搜索引擎后端。可以通过在settings.py中添加HAYSTACK_CONNECTIONS配置项来完成此操作。将以下内容添加到settings.py文件中:
HAYSTACK_CONNECTIONS = {'default': {'ENGINE': 'haystack.backends.solr_backend.SolrEngine','URL': 'http://127.0.0.1:8983/solr/blog'},
}
注意,URL指向我们的blog应用的Solr内核。Haystack现已安装完毕,可与Solr一起使用。
3.4 为搜索引擎添加索引
现在,我们必须在搜索引擎中注册要存储的模型。Haystack的惯例是在应用程序中创建一个search_indexs.py文件,并在那里注册模型。在blog应用程序目录中创建一个新文件,并将其命名为search_indexs.py。向其中添加以下代码:
from haystack import indexes
from .models import Postclass PostIndex(indexes.SearchIndex, indexes.Indexable):text = indexes.CharField(document=True, use_template=True) publish = indexes.DateTimeField(model_attr='publish')def get_model(self):return Postdef index_queryset(self, using=None):return self.get_model().published.all()
这里我们需要为Post模型(博客帖子模型)对应的数据表建立索引。首先我们自定义一个索引类,在这个类中需要告诉Haystack搜索的字段,以及搜索这个操作对应的查询集。索引是通过将indexs.SearchIndex和indexs.Indexeble子类化而生成的。每个SearchIndex都要求其字段之一的document=True。惯例是将此字段命名为text ,此字段是主搜索字段。当use_template=True时,我们正在告诉Haystack,该字段将被呈现在模板中,用以构建搜索引擎将药索引的文档。publish字段是一个时间字段,它也将被索引。我们使用model_attr参数表示该字段对应的是Post模型的publish 字段。该字段将使用索引的Post对象的publish字段的内容进行索引。
get_model()方法必须返回建立索引数据表对应的模型,此处我们是需要搜索帖子的内容,所以返回的就是Post模型。index_queryset()方法返回将被索引的对象的查询集queryset。请注意,我们只搜索已发布的帖子。
现在,在博客应用程序的templates目录中创建以下文件search/indexs/blog/post_text.txt,并向其中添加以下代码:
{{ object.title }}
{{ object.tags.all|join:", " }}
{{ object.body }}
这是我们的text字段的文档模板的默认路径,Haystack使用应用程序名称和模型名称动态构建路径。每次我们要索引一个对象时,Haystack都会基于该模板创建一个文档,然后Solr搜索引擎中对文档进行索引。[这个地方的索引就是搜索的意思]
现在我们有了自定义搜索索引,我们必须创建适当的Solr模式。Solr的配置是基于XML的,因此我们必须将要索引的数据变成XML格式。幸运的是,Haystack提供了一种基于搜索索引动态生成Solr模式的方法。打开终端并运行以下命令:
python manage.py build_solr_schema
执行上述代码,您将看到一个XML输出。在生成的XML代码的底部,您将看到Haystack自动为帖子(Post)的索引生成对应的字段:
<field name="text" type="text_en" indexed="true" stored="true" multiValued="false" />
<field name="publish" type="date" indexed="true" stored="true" multiValued="false" />
复制全部XML代码,该XML是将数据索引到Solr中的模式。将新模式粘贴到Solr安装示例目录中的blog/conf/schema.xml文件中。schema.xml文件包含在本章附带的代码中,因此您也可以直接从该文件复制它。
在浏览器中打开http://127.0.0.1:8983/solr/,单击Core Admin菜单选项卡,然后单击blog core,然后单击Reload按钮:

我们重新加载内核,当核心完成重载时,就可以用新模式索引新数据了。
3.5 索引数据
让我们把我们博客的帖子索引到Solr中。打开终端,执行如下命令:
python manage.py rebuild_index
你将看到以下警告:
WARNING: This will irreparably remove EVERYTHING from your search index in connection 'default'.
Your choices after this are to restore from backups or rebuild via the `rebuild_index` command.
Are you sure you wish to continue? [y/N]
输入y之后回车。Haystack将清除搜索索引,并插入所有已发表的博客。你应该看到如下输出:
Removing all documents from your index because you said so. All documents removed.
Indexing 4 posts
在浏览器中打开http://127.0.0.1:8983/solr/#/blog。您将看到索引文档的数量,如下所示:

现在,在浏览器中打开http://127.0.0.1:8983/solr/#/blog/query。这是Solr提供的查询接口。单击Execute查询按钮。默认查询请求在核心中索引的所有文档。您将看到带有查询结果的JSON输出。输出的文档如下所示:
{"id": "blog.post.1","text": "Who was Django Reinhardt?\njazz, music\nThe Django web framework was named after the amazing jazz guitarist Django Reinhardt.","django_id": "1","publish": "2015-09-20T12:49:52Z", "django_ct": "blog.post"
},
这是为搜索索引中的每个帖子存储的数据。文本字段包含标题、用逗号分隔的标签和文章正文,因为这个字段是用我们之前定义的模板构建的。
您已经使用python manage.py rebuild_index删除索引中的所有内容,并重新索引文档。要在不删除所有对象的情况下更新索引,可以使用python manage.py update_index。或者,您可以使用参数–age=<num_hours>来更新更少的对象。您可以为此索引设置一个Cron作业,以保持Solr索引的更新。
3.6 创建搜索视图
现在,我们将创建一个视图,允许用户搜索帖子。首先,我们需要一个搜索表单。编辑blog应用程序下的forms.py文件并添加以下内容:
class SearchForm(forms.Form):query = forms.CharField()
我们将使用查询字段让用户引入搜索词。编辑blog应用程序的views.py文件,并添加以下代码:
from .forms import EmailPostForm, CommentForm, SearchForm
from haystack.query import SearchQuerySetdef post_search(request):form = SearchForm()if 'query' in request.GET:form = SearchForm(request.GET)if form.is_valid():cd = form.cleaned_dataresults = SearchQuerySet().models(Post).filter(content=cd['query']).load_all() # count total resultstotal_results = results.count()return render(request,'blog/post/search.html',{'form': form,'cd': cd,'results': results,'total_results': total_results})
在这个视图中,首先实例化之前创建的搜索表单SearchForm。我们将使用GET方法提交表单,以便生成的URL包含查询参数。我们将通过查询请求中的查询参数来确定表单是否已提交。当表单提交时,我们用提交的GET数据实例化它,并检查给定的数据是否有效。如果表单有效,则使用SearchQuerySet对主要内容包含给定查询的索引Post对象执行搜索。load_all()方法一次从数据库加载所有相关的Post对象。使用这种方法,我们用数据库对象填充搜索结果,以避免在迭代结果以访问对象数据时对数据库的每个对象访问。最后,我们将结果的总数存储在total_results变量中,并将局部变量作为上下文传递给模板。
完成搜索视图的创建。我们需要创建一个模板,以便在用户执行搜索时显示表单和结果。在templates/blog/post/目录中创建一个新文件,命名为search.html,并添加以下代码:
{% extends "blog/base.html" %}{% block title %}Search{% endblock %}{% block content %}{% if "query" in request.GET %}<h1>Posts containing "{{ cd.query }}"</h1><h3>Found {{ total_results }} result{{ total_results|pluralize }}</h3>{% for result in results %}{% with post=result.object %}<h4><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h4>{{ post.body|truncatewords:5 }}{% endwith %}{% 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 %}
与在搜索视图中的逻辑一样,我们根据查询参数的存在来区分表单是否已提交。在提交文章之前,我们显示表单和提交按钮。提交帖子后,我们显示执行的查询、结果总数和结果列表。每个结果都是由Solr返回并由Haystack封装的文档。我们需要使用结果。对象来访问与此结果相关的实际Post对象。
最后,编辑你的博客应用程序的urls.py文件,并添加以下URL模式:
url(r'^search/$', views.post_search, name='post_search'),
现在,在浏览器中打开http://127.0.0.1:8000/blog/search/。你会看到一个这样的搜索表单:

现在,输入一个查询并单击Search按钮。你会看到搜索查询的结果,就像这样:

现在,您的项目中已经内置了一个强大的搜索引擎,但是从这里开始,您可以使用Solr和Haystack做很多事情。Haystack包括搜索视图、表单和搜索引擎的高级功能。您可以在http://django-haystack.readthedocs.org/en/latest/上阅读Haystack文档。
Solr搜索引擎可以通过自定义模式来适应任何需求。您可以组合执行的分析器、标记器和标记过滤器索引或搜索时间,为您的网站内容提供更准确的搜索。你可以在https://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters上看到所有的使用方法。
4 总结
在本章中,你学习了如何创建自定义的Django模板标签和过滤器,为模板提供自定义功能。您还创建了一个站点地图供搜索引擎抓取您的站点,并创建了一个RSS提要供用户订阅。您还通过将Solr与Haystack集成到项目中,为您的博客构建了一个搜索引擎。
在下一章中,你将学习如何通过使用Django认证框架、创建自定义用户配置文件和构建社交认证来构建一个社交网站。
相关文章:
Django by Example·第三章|Extending Your Blog Application@笔记
Django by Example第三章|Extending Your Blog Application笔记 之前已经写过两章内容了,继续第三章。第三章继续对博客系统的功能进行拓展,其中将会穿插一些重要的技术要点。 部分内容引用自原书,如果大家对这本书感兴趣 请支持原版Django …...
23.2.13 Drive development 设备树信息解析相关代码
1.练习课上代码 2.把设备树信息解析相关函数按照自己的理解发布CSDN 3.复习中断相关内核 IO多路复用---epoll 核心内容:一棵树一个链表三个方法 epoll会将要监听的事件文件描述符添加到内核里一颗红黑树上,当有事件发生,epoll会调用回调函数…...
智能工厂以MES系统为基础,实现"信息化减人,自动化换人"
MES是一种生产信息化的管理系统,它适用于制造业的车间实施层面。MES能够为企业提供生产数据、项目看板、库存、成本、工装、生产计划、计划排程、质量、人力资源、采购、生产过程控制、底层数据集成分析、上层数据集成分解等管理模块,为企业打造一个扎实…...
【数据挖掘实战】——电力窃漏电用户自动识别
【数据挖掘实战】——电力窃漏电用户自动识别一、背景和挖掘目标二、分析方法与过程1、初步分析2、数据抽取3、探索分析4、数据预处理5、构建专家样本三、构建模型1、构建窃漏电用户识别模型2、模型评价3、进行窃漏电诊断拓展思考项目代码地址:https://gitee.com/li…...
树莓派 安装 宝塔linux面板5.9. 2023-2-13
一.环境 1.硬件环境: 树莓派3b , 8GB tf卡 ,micro usb电源 2.网络环境: 网线直连路由器 , 可访问互联网 3.软件环境: 树莓派操作系统 CentOS-Userland-7-armv7hl-RaspberryPI-Minimal-2009-sda(linux) 系统刻录工具 Win32DiskImager (win) ip扫描工具 Adv…...
如何提高短视频的播放量-4个技巧
做短视频自媒体,点击率是第一位,点击量越多,粉丝也就越多。可是,怎么才能增加短视频的点击率和提高播放量呢?今天就来教大家4个技巧: 1、蹭热点 热门话题自带流量,它的热度和价值,是…...
搜索二叉树
文章目录二叉搜索树模拟实现InsertInsertR()EraseEraseR搜索树的价值实现代码二叉搜索树 在二叉树的基础之上, 左子树的值都比根节点小,右子树都更大。那么他的左右子树也分别叫做二叉搜索树。 查找一个节点,最多查找高度次(建立在这个树是比较均衡的).10亿里面找…...
CentOS8基础篇5:用户账号与用户组的创建
一、用户与用户组概念 Linux是一个多用户、多任务的服务器操作系统,多用户多任务指可以在系统上建立多个用户,而多个用户可以在同一时间内登录同一个系统执行各自不同的任务,而互不影响。 Linux用户是根据角色定义的,具体分为三…...
阿里云服务器使用
服务器配置CPU&内存:2核(vCPU)2 GiB操作系统:Ubuntu 22.04 64位运行环境部署因为部署用到了nodejs首先,打开终端,并输入以下命令以安装必要的软件包:sudo apt-get install curl接着,使用 curl 命令安装…...
全国空气质量排行,云贵川和西藏新疆等地空气质量更好
哈喽,大家好,春节刚刚过去,不知道大家是不是都开始进入工作状态了呢?春节期间,允许燃放烟花爆竹的地区的朋友们不知道都去欣赏烟花表演没有?其他地区的朋友们相比烟花表演可能更关心燃放烟花爆竹造成的环境…...
Learning C++ No.8【内存管理】
引言: 北京时间:2023/2/12/18:04,昨天下午到达学校,摆烂到现在,该睡睡,该吃吃,该玩玩,在一顿操作之下,目前作息调整好了一些,在此记录,2月11&…...
『 MySQL篇 』:MySQL表的相关约束
基础篇 MySQL系列专栏(持续更新中 …)1『 MySQL篇 』:库操作、数据类型2『 MySQL篇 』:MySQL表的CURD操作3『 MySQL篇 』:MySQL表的相关约束文章目录 1 . 非空约束 (not null)2 . 唯一性约束(unique)3 . check约束4 . 默认约束(default)5 . 主…...
家政服务小程序实战教程10-分类展示
小程序一般底部菜单栏会有一个分类的功能,点击分类,以侧边栏导航的形式列出所有类目,点击某个类目可以做数据筛选,我们本篇就实现一下该功能 01 优化数据源 在我们家政服务小程序里,我们已经建立了类型和服务的数据源…...
一篇文章带你学会Ansible的安装及部署
目录 前言 一、什么是Ansible 二、Ansible的工作方式 三、Ansible的安装 四、构建Anisble清单 1、清单书写方式 2、清单查看 3、清单书写规则 4、主机规格的范围化操作 五、ansible命令指定清单的正则表达式 六、 Ansible配置文件参数详解 1、配置文件的分类与优先…...
opencv常用函数
1)读视频 img cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) if vc.isOpened():ret, frame vc.read() else:ret False while ret:#此处省略具体的操作ret, frame vc.read() # 读下一帧 vc.release() 2)保存视频 def mk_video_writer(vc, path,frame_…...
Java集合框架常见面试题
1. 剖析面试最常见问题之 Java 集合框架 1.1. 集合概述 1.1.1. Java 集合概览1.1.2. 说说 List,Set,Map 三者的区别?1.1.3. 集合框架底层数据结构总结 1.1.3.1. List1.1.3.2. Set1.1.3.3. Map 1.1.4. 如何选用集合?1.1.5. 为什么要使用集合? 1.2. Colle…...
医用雾化器单片机方案设计
产品概述 雾化器是一款基于电路板的振荡信号被大功率三极管进行能量放大,传递给压电陶瓷片,当压电陶瓷片受电信号的激励,产生高频谐振,并使吸附在微孔膜上的液体结产生超声振荡,将液体的结构打散而产生自然飘逸的雾。不…...
python魔术方法(一)
所谓的魔术方法就是让用户客制化类的方法,常常是python中开头有两个下划线的方法。 __new__() new是创建一个类的过程 class A:def __new__(cls,x):print("__new__")return super().__new__(cls)由于new函数是建立了一个对象,所以必须返回一…...
IDEA配置部署tomcat详细步骤(maven web 和Javaweb)
目录 读者手册 一、概念与准备工作 (一)概念 (二)准备工作 (三)IDEA配置tomcat服务器(maven web项目演示) ( 四)Javaweb项目创建tomcat演示 读者手册 读…...
没有设置密码,每次打开RAR文件却都要输密码?
有小伙伴说遇到这种情况:用WinRAR软件压缩RAR文件后,再次打开时显示需要输入密码,但自己压缩文件时并没有设置密码,后续不管几次压缩文件都需要密码,这是怎么回事呢? 其实,这很可能是之前设置压…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
