Django by Example·第三章|Extending Your Blog Application@笔记

news2024/9/23 17:14:54

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 Post

register = 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
*	Three

And a [link to the Django website](https://www.djangoproject.com/)

打开浏览器,查看帖子的呈现方式。您应该看到以下内容:
在这里插入图片描述
如您所见,自定义模板过滤器对于自定义格式非常有用。你可以在以下位置找到有关自定义过滤器的详细信息编写自定义模板过滤器。


2 为你的网站添加站点地图

sitemap又称“网站地图”,是展示一个网站结构、栏目和内容说明等基本信息的文档,就像人们对一个陌生城市的了解需要借助于城市地图一样,对于一个网站信息的快速了解也可以借助于网站地图进行。

Django附带了一个站点地图框架,它允许您动态生成站点地图。站点地图是一个XML文件,它告诉搜索引擎网站的页面、它们的相关性以及更新的频率。通过使用站点地图,您将帮助爬虫为您的网站内容编制索引。

Django站点地图框架依赖于Django.contrib.sites,它允许您将对象关联到与项目一起运行的特定网站。当您想使用一个Django项目运行多个站点时,这很方便。要安装站点地图框架,我们需要激活项目中的站点和站点地图应用程序。编辑项目的settings.py文件,并将django.contrib.sitesdjango.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 Post

class PostSitemap(Sitemap):
	changefreq = 'weekly'
	priority = 0.9
    
    def 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 PostSitemap

sitemaps = {
	'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 Post

class 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.title
    
    def 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 LatestPostsFeed

urlpatterns = [
	# ... 
    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运行页面
这是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 Post

class PostIndex(indexes.SearchIndex, indexes.Indexable):
	text = indexes.CharField(document=True, use_template=True)    
	publish = indexes.DateTimeField(model_attr='publish')
    
    def get_model(self):
    	return Post
    
    def 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 SearchQuerySet

def post_search(request):
	form = SearchForm()
    if 'query' in request.GET:
    	form = SearchForm(request.GET)
    	if form.is_valid():
    		cd = form.cleaned_data
    		results = SearchQuerySet().models(Post).filter(content=cd['query']).load_all() # count total results
    		total_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认证框架、创建自定义用户配置文件和构建社交认证来构建一个社交网站。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/342710.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于模块联邦的微前端实现方案

一、 微前端应用案例概述 当前案例中包含三个微应用&#xff0c;分别为 Marketing、Authentication 和 Dashboard Marketing&#xff1a;营销微应用&#xff0c;包含首页组件和价格组件 Authentication&#xff1a;身份验证微应用&#xff0c;包含登录组件 Dashboard&#x…

B站发帖软件哪个好用?好用的哔哩哔哩发帖工具

B站发帖软件哪个好用?好用的哔哩哔哩发帖工具#发帖软件#哔哩哔哩发帖#视频发布软件 登录成功之后&#xff0c;进入到这样一个界面&#xff0c;默认情况下是这个样子的&#xff0c;我们在这里输入一下我们的一个文件夹的路径&#xff0c;输入到这里&#xff0c;点击添加账号&a…

kettle开发-Day36-循环驱动作业

前言&#xff1a;在日常数据处理时&#xff0c;我们通过变量传参来完成某个日期的数据转换。但可能因程序或者网络原因导致某个时间段的数据抽取失败。常见导致kettle作业失败的原因大概分为三大类&#xff0c;数据源异常、数据库异常、程序异常。因此面对这些异常时&#xff0…

Not available OpenAI s services are not available in your country.

一、准备阶段 1、邮箱账号(qq、网易、谷歌等等) 2、你能够科学上网(下边详细介绍) 3、拥有一个GW手机号&#xff0c;用于接收注册验证码。&#xff08;下边详细介绍&#xff09; 二、开始注册 1、官方注册网址https://beta.openai.com/signup&#xff08;按照步骤注册&am…

RDSDRDSPolarDBPolarDB-X的区别

RDS 阿里云关系型数据库&#xff08;Relational Database Service&#xff0c;简称RDS&#xff09;&#xff0c;是一种稳定可靠、可弹性伸缩的在线数据库服务。 基于阿里云分布式文件系统和高性能存储&#xff0c;RDS支持MySQL、SQL Server、PostgreSQL和PPAS&#xff08;Post…

在VScode终端上创建nuxtjs项目遇到的问题以及使用GitHub遇到的问题和一些个人笔记

文章目录&#x1f4cb;前言&#x1f4bb;关于GitHub打开慢或无法打开的问题&#x1f4bb;克隆GitHub的项目到本地&#x1f4bb;创建nuxtjs项目&#x1f9e9;无法加载文件的报错问题&#x1f9e9;使用vue init nuxt/starter demo出现的问题&#x1f9e9;另一种命令创建nuxtjs项目…

字符编码及转换

什么是字符编码字符编码&#xff08;Character encoding&#xff09;也称字集码&#xff0c;是把字符集中的字符&#xff0c;编码为指定集合中的某一对象&#xff08;例如&#xff1a;比特模式、自然数序列、8位组或者电脉冲&#xff09;&#xff0c;以便文本在计算机中存储或者…

前端控制台出现 红色 的报错信息TypeError: fn.apply is not a function

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 使用vue在原来的系统中 开发新的模块&#xff0c;开发时无意间发现 前端控制台出现 红色 的报错信息&#xff0c;但是页面依然还是正常 加载出来了 控制台一直报错 TypeError: fn.apply is not a fun…

09- 逻辑回归算法 (LogisticRegression) (机器学习)

基本概念: 逻辑回归主要逻辑是通过sigmoid函数进行分类, 当函数结果大于0时赋值1, 小于0时赋值0, 然后根据结果进行分类, 化简后求最小值的过程和线性方程类似, 该函数的特点是:分类算法 模型训练 : lr LogisticRegression() from sklearn.linear_model import LogisticRegr…

力扣39.组合总数

文章目录力扣39.组合总数题目描述方法1&#xff1a;深搜回溯力扣39.组合总数 题目描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可…

【数据库】MySQL 单表查询,多表查询

目录 单表查询 一&#xff0c;创建表worker 1&#xff0c;创建表worker的sql代码如下&#xff1a; 2&#xff0c;向worker表中插入信息 二&#xff0c; 按要求进行单表查询 1、显示所有职工的基本信息。 2、查询所有职工所属部门的部门号&#xff0c;不显示重复的部门号。 …

【自学Python】Python字符大小写判断

大纲 Python字符串是否是小写 Python字符串是否是小写教程 在开发过程中&#xff0c;有时候我们需要判断一个 字符串 是否是小写形式&#xff08;即&#xff0c;所有的字符都是小写字母&#xff0c;不是英文字符的忽略不做判断&#xff09;&#xff0c;在 Python 中&#xff…

SAP ERP系统SD模块常用增强之一:VA01/VA02创建或修改SO的输入检查

在SAP/ERP项目的实施中销售管理模块&#xff08;SD&#xff09;的创建和修改销售订单必定会有输入字段校验检查的需求&#xff0c;来防止业务人员录入错误或少录入数据&#xff0c;SAP公司也考虑到这一点&#xff0c;所以这方面的配置功能也非常强大&#xff0c;通常情况下不需…

GraphQL vs REST API 架构,谁更胜一筹?

GraphQL vs REST API 架构&#xff0c;谁更胜一筹&#xff1f; Michele Moody 平川 万佳 2020 年 1 月 18 日 本文字数&#xff1a;2890 字 阅读完需&#xff1a;约 9 分钟 2015 年&#xff0c;Facebook 开源 GraphQL。此后&#xff0c;它在前端 Web 中大受欢迎。传统的 R…

一篇文章带你学会Ansible的安装及部署

目录 前言 一、什么是Ansible 二、Ansible的工作方式 三、Ansible的安装 四、构建Anisble清单 1、清单书写方式 2、清单查看 3、清单书写规则 4、主机规格的范围化操作 五、ansible命令指定清单的正则表达式 六、 Ansible配置文件参数详解 1、配置文件的分类与优先…

【C++】C/C++内存管理模板初阶

文章目录一、 C/C内存管理1. C/C内存分布2. C内存管理方式3. operator new与operator delete函数4. new和delete的实现原理5. 定位new表达式6. 常见面试题malloc/free和new/delete的区别内存泄漏二、模板初阶1. 泛型编程2. 函数模板3. 类模板一、 C/C内存管理 1. C/C内存分布 …

每天10个前端小知识 【Day 13】

前端面试基础知识题 1. Position&#xff1a;absolute绝对定位&#xff0c;是相对于谁的定位&#xff1f; CSS position属性用于指定一个元素在文档中的定位方式。top&#xff0c;right&#xff0c;bottom 和 left 属性则决定了该元素的最终位置。 absolute的元素会被移出正…

牛客网 NC107 寻找峰值

前言&#xff1a;内容包括四大模块&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路&#xff0c;代码解读 题目&#xff1a; 描述 给定一个长度为n的数组nums&#xff0c;请你找到峰值并返回其索引。数组可能包含多个峰值&#xff0c;在这种情况下&#xff0c;返回…

vue-router路由配置

介绍&#xff1a;路由配置主要是用来确定网站访问路径对应哪个文件代码显示的&#xff0c;这里主要描述路由的配置、子路由、动态路由&#xff08;运行中添加删除路由&#xff09; 1、npm添加 npm install vue-router // 执行完后会自动在package.json中添加 "vue-router…

某游戏辅助功能分析

FPS游戏发展至今&#xff0c;阻挡外挂开发者脚步的往往不是数据和功能开发&#xff0c;而是高难度的检测。 现如今&#xff0c;检测的手段越来越多&#xff0c;也越来越五花八门。列如&#xff1a; 检测参数&#xff0c;检测堆栈&#xff0c;检测注入等等。 CRC是众多检测手段中…