django搭建个人博客

Posted brafei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了django搭建个人博客相关的知识,希望对你有一定的参考价值。

第三章

扩展你的blog应用

在上一章中我们学习了表单的基础以及如何在项目中集成第三方的应用。本章将会包含以下内容:

  • 创建自定义的模板标签(template tags)和过滤器(filters)

  • 添加一个站点地图和帖子反馈(post feed)

  • 使用Solr和Haystack构建一个搜索引擎

创建自定义的模板标签(template tags)和过滤器(filters)

Django提供了很多内置的模板标签(tags),例如{% if %}或者{% block %}。你已经在你的模板(template)中使用过一些了。你可以在https://docs.djangoproject.com/en/1.8/ ref/templates/builtins/ 中找到关于内置模板标签(template tags)以及过滤器(filter)的完整参考。

当然,Django也允许你创建自己的模板标签(template tags)来执行自定义的动作。当你需要在你的模板中添加功能而Django模板标签(template tags)的核心设置无法提供此功能的时候,自定义模板标签会非常方便。

创建自定义的模板标签(template tags)

Django提供了以下帮助函数(functions)来允许你以一种简单的方式创建自己的模板标签(template tags):

  • simple_tag:处理数据并返回一个字符串(string)

  • inclusion_tag:处理数据并返回一个渲染过的模板(template)

  • assignment_tag:处理数据并在上下文(context)中设置一个变量(variable)

模板标签(template tags)必须存在Django的应用中。

进入你的blog应用目录,创建一个新的目录命名为templatetags然后在该目录下创建一个空的init.py文件。接着在该目录下继续创建一个文件并命名为blog_tags.py。到此,我们的blog应用文件结构应该如下所示:

blog/
    __init__.py
    models.py    ...
    templatetags/
        __init__.py
        blog_tags.py

文件的命名是非常重要的。你将在模板(template)中使用这些模块的名字加载你的标签(tags)。

我们将要开始创建一个简单标签(simple tag)来获取blog中所有已发布的帖子。编辑你刚才创建的blog_tags.py文件,加入以下代码:

from django import template

register = template.Library()from ..models import Post@register.simple_tagdef total_posts():
    return Post.published.count()

我们已经创建了一个简单的模板标签(template tag)用来取回目前为止所有已发布的帖子。每一个模板标签(template tags)都需要包含一个叫做register的变量来表明自己是一个有效的标签(tag)库。这个变量是template.Library的一个实例,它是用来注册你自己的模板标签(template tags)和过滤器(filter)的。我们用一个Python函数定义了一个名为total_posts的标签,并用@register.simple-tag装饰器定义此函数为一个简单标签(tag)并注册它。

Django将会使用这个函数名作为标签(tag)名。如果你想使用别的名字来注册这个标签(tag),你可以指定装饰器的name属性,比如@register.simple_tag(name='my_tag')

在添加了新的模板标签(template tags)模块后,你必须重启Django开发服务才能使用新的模板标签(template tags)和过滤器(filters)。

在使用自定义的模板标签(template tags)之前,你必须使用{% load %}标签在模板(template)中来加载它们才能有效。就像之前提到的,你需要使用包含了你的模板标签(template tags)和过滤器(filter)的Python模块的名字。打开blog/base.html模板(template)在顶部添加{% load blog_tags %}加载你自己的模板标签(template tags)模块。之后使用你创建的标签(tag)来显示你的帖子总数。只需要在你的模板(template)中添加{% total_posts %}。最新的模板(template)看上去如下所示:

{% 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/ 。你会在站点的侧边栏(sidebar)看到帖子的总数,如下所示:

自定义模板标签(template tags)的作用是你可以处理任何的数据并且在任何模板(template)中添加它而不用去关心视图(view)执行。你可以在你的模板(template)中运行查询集(QuerySets)或者处理任何数据展示结果。

现在,我们要创建另外一个标签(tag),可以在我们blog的侧边栏(sidebar)展示最新的几个帖子。这一次我们要使用一个包含标签(inclusion tag)。使用一个包含标签(inclusion tag),你就可以利用模板标签(template tags)返回的上下文变量(context variables)来渲染模板(template)。编辑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*注册模板标签(template tag),指定模板(template)必须被blog/post/latest_posts.html返回的值渲染。我们的模板标签(template tag)将会接受一个可选的count参数(默认是5)允许我们指定我们想要显示的帖子数量。我们使用这个变量来限制Post.published.order_by('-publish')[:count]查询的结果。请注意,这个函数返回了一个字典变量而不是一个简单的值。包含标签(inclusion tags)必须返回一个字典值,作为上下文(context)来渲染特定的模板(template)。包含标签(inclusion tags)返回一个字典。这个我们刚创建的模板标签(template tag)可以通过传入可选的评论数量值来使用显示,类似*{% show_latest_posts 3 %}。

现在,在blog/post/下创建一个新的模板(template)文件并且命名为latest_posts.html。在该文件中添加如下代码:

<ul>{% for post in latest_posts %}  <li>    <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>  </li>{% endfor %}</ul>

在这里,我们使用模板标签(template tag)返回的latest_posts变量展示了一个无序的帖子列表。现在,编辑blog/base.hmtl模板(template)添加这个新的模板标签(template tag)来展示最新的3条帖子。侧边栏(sidebar)区块(block)看上去应该如下所示:

<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>

这个模板标签(template tag)被调用而且传入了需要展示的帖子数(原文此处 number of comments,应该是写错了)。当前模板(template)在给予上下文(context)的位置会被渲染。

现在,回到浏览器刷新这个页面,侧边栏应该如下图所示:

django搭建个人博客(四)

最后,我们来创建一个分配标签(assignment tag)。分配标签(assignment tag)类似简单标签(simple tags)但是他们将结果存储在给予的变量中。我们将会创建一个分配标签(assignment tag)来展示拥有最多评论的帖子。编辑blog_tags.py文件,在其中添加如下导入和模板标签:

from django.db.models import Count@register.assignment_tagdef get_most_commented_posts(count=5):
    return Post.published.annotate(
                total_comments=Count('comments')
            ).order_by('-total_comments')[:count]    

这个查询集(QuerySet)使用annotate()函数,为了进行聚合查询,使用了Count聚合函数。我们构建了一个查询集(QuerySet),聚合了每一个帖子的评论总数并保存在total_comments字段中,接着我们通过这个字段对查询集(QuerySet)进行排序。我们还提供了一个可选的count变量,通过给定的值来限制返回的帖子数量。

除了Count以外,Django还提供了不少聚合函数,例如Avg,Max,Min,Sum.你可以在 https://docs.djangoproject.com/en/1.8/topics/db/aggregation/ 页面读到更多关于聚合方法的信息。

编辑blog/base.html模板(template),在侧边栏(sidebar)<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>

使用分配模板标签(assignment template tags)的方法是{% template_tag as variable %}。对于我们的模板标签(template tag)来说,我们使用{% get_most_commented_posts as most_commented_posts %}。 这样,我们可以存储这个模板标签(template tag)返回的结果到一个新的名为most_commented_posts变量中。之后,我们就可以用一个无序列表(unordered list)显示返回的帖子。

现在,打开浏览器刷新页面来看下最终的结果,应该如下图所示:

django搭建个人博客(四)

你可以在 https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/ 页面得到更多的关于自定义模板标签(template tags)的信息。

创建自定义的模板过滤器(template filters)

Django拥有多种内置的模板过滤器(template filters)允许你对模板(template)中的变量进行修改。这些过滤器其实就是Python函数并提供了一个或两个参数————一个是需要处理的变量值,一个是可选的参数。它们返回的值可以被展示或者被别的过滤器(filters)处理。一个过滤器(filter)类似{{ variable|my_filter }}或者再带上一个参数,类似{{ variable|my_filter:"foo" }}。你可以对一个变量调用你想要的多次过滤器(filter),类似{{ variable|filter1|filter2 }}, 每一个过滤器(filter)都会对上一个过滤器(filter)输出的结果进行过滤。

我们这就创建一个自定义的过滤器(filter),可以在我们的blog帖子中使用markdown语法,然后在模板(template)中将帖子内容转变为HTML。Markdown是一种非常容易使用的文本格式化语法并且它可以转变为HTML。你可以在 http://daringfireball.net/projects/markdown/basics 页面学习这方面的知识。

首先,通过pip渠道安装Python的markdown模板:

pip install Markdown==2.6.2

之后编辑blog_tags.py文件,包含如下代码:

from django.utils.safestring import mark_safeimport markdown@register.filter(name='markdown')def markdown_format(text):
    return mark_safe(markdown.markdown(text))

我们使用和模板标签(template tags)一样的方法来注册我们自己的模板过滤器(template filter)。为了避免我们的函数名和markdown模板名起冲突,我们将我们的函数命名为markdown_format,然后将过滤器(filter)命名为markdown,在模板(template)中的使用方法类似{{ variable|markdown }}。Django会转义过滤器(filter)生成的HTML代码。我们使用Django提供的mark_safe方法来标记结果,在模板(template)中作为安全的HTML被渲染。默认的,Django不会信赖任何HTML代码并且在输出之前会进行转义。唯一的例外就是被标记为安全转义的变量。这样的操作可以阻止Django从输出中执行潜在的危险的HTML,并且允许你创建一些例外情况只要你知道你正在运行的是安全的HTML。

现在,在帖子列表和详情模板(template)中加载你的模板标签(template tags)模块。在post/list.htmlpost/detail.html模板(template)的顶部{% extends %}的后方添加如下代码:

{% load blog_tags %}

post/detail.thml模板中,替换以下内容:

{{ post.body|linebreaks }}

替换成:

{{ post.body|markdown }}

之后,在post/list.html文件中,替换以下内容:

{{ post.body|truncatewords:30|linebreaks }}

替换成:

{{ post.body|markdown|truncatewords_html:30 }}

truncateword_html过滤器(filter)会在一定数量的单词后截断字符串,避免没有关闭的HTML标签(tags)。

现在,打开 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/)

在浏览器中查看帖子的渲染情况,你会看到如下图所示:

django搭建个人博客(四)

就像你所看到的,自定义的模板过滤器(template filters)对于自定义的格式化是非常有用的。你可以在 https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/#writing-custom-templatefilters 页面获取更多关于自定义过滤器(filter)的信息。

为你的站点添加一个站点地图(sitemap)

Django自带一个站点地图(sitemap)框架,允许你为你的站点动态生成站点地图(sitemap)。一个站点地图(sitemap)是一个xml文件,它会告诉搜索引擎你的网站中存在的页面,它们的关联和它们更新的频率。使用站点地图(sitemap),你可以帮助网络爬虫(crawlers)来对你的网站内容进行索引和标记。

Django站点地图(sitemap)框架依赖django.contrib.sites模块,这个模块允许你将对象和正在你项目运行的特殊网址关联起来。当你想用一个单独Django项目运行多个网站时,这是非常方便的。为了安装站点地图(sitemap)框架,我们需要在项目中激活sitessitemap这两个应用。编辑项目中的settings.py文件在INSTALLED_APPS中添加django.contrib.sitesdjango.contrib.sitemaps。之后为站点ID定义一个新的设置,如下所示:

SITE_ID = 1# Application definitionINSTALLED_APPS = (# ...'django.contrib.sites','django.contrib.sitemaps',
)

现在,运行以下命令在数据库中为Django的站点应用创建所需的表:

python manage.py migrate

你会看到如下的输出内容:

Applying sites.0001_initial... OK

sites应用现在已经在数据库中进行了同步。现在,在你的blog应用目录下创建一个新的文件命名为sitemaps.py。打开这个文件,输入以下代码:

from django.contrib.sitemaps import Sitemapfrom .models import Postclass PostSitemap(Sitemap):
    changefreq = 'weekly'
    priority = 0.9
    def items(self):
        return Post.published.all()    def lastmod(self, obj):
        return obj.publish        

通过继承sitemaps模块提供的Sitemap类我们创建一个自定义的站点地图(sitemap)。changefreqpriority属性表明了帖子页面修改的频率和它们在网站中的关联性(最大值是1)。items()方法返回了在这个站点地图(sitemap)中所包含对象的查询集(QuerySet)。默认的,Django在每个对象中调用get_absolute_url()方法来获取它的URL。请记住,这个方法是我们在第一章(创建一个blog应用)中创建的,用来获取每个帖子的标准URL。如果你想为每个对象指定URL,你可以在你的站点地图(sitemap)类中添加一个location方法。lastmode方法接收items()返回的每一个对象并且返回对象的最后修改时间。changefreqpriority两个方法既可以是方法也可以是属性。你可以在Django的官方文档 https://docs.djangoproject.com/en/1.8/ref/contrib/sitemaps/ 页面中获取更多的站点地图(sitemap)参考。

最后,我们只需要添加我们的站点地图(sitemap)URL。编辑项目中的主*urls.py文件,如下所示添加站点地图(sitemap):

from django.conf.urls import include, urlfrom django.contrib import adminfrom django.contrib.sitemaps.views import sitemapfrom 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'),
]

在这里,我们加入了必要的导入并定义了一个sitemaps的字典。我们定义了一个URL模式来匹配sitemap.xml并使用sitemap视图(view)。sitemaps字典会被传入到sitemap视图(view)中。现在,在浏览器中打开 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-djangoreinhardt/</loc>        <lastmod>2015-09-20</lastmod>        <changefreq>weekly</changefreq>        <priority>0.9</priority>    </url></urlset>

调用get_absolute_url()方法,每个帖子的URL已经被构建。如同我们之前在站点地图(sitemap)中所指定的,lastmode属性对应该帖子的publish日期字段,changefreqpriority属性也从我们的PostSitemap类中带入。你能看到被用来构建URL的域(domain)是example.com。这个域(domain)来自存储在数据库中的一个Site对象。这个默认的对象是在我们之前同步sites框架数据库时创建的。在你的浏览器中打开http://127.0.0.1:8000/admin/sites/site/ ,你会看到如下图所示:

django搭建个人博客(四)

这是sites框架管理视图(admin view)的列表显示。在这里,你可以设置sites框架使用的域(domain)或者主机(host),而且应用也依赖它们。为了生成能在我们本地环境可用的URL,更改域(domain)名为127.0.0.1:8000,如下图所示并保存:

为了开发需要我们指向了我们本地主机。在生产环境中,你必须使用你自己的sites框架域(domain)名。

为你的blog帖子创建feeds

译者注:这节中有不少英文单词,例如feed,syndication,Atom等,没有比较好的翻译,很多地方也都是直接不翻译保留原文,所以我也保留原文)

Django有一个内置的syndication feed框架,就像用sites框架创建站点地图(sitemap)一样,使用类似的方式(manner),你可以动态(dynamically)生成RSS或者Atom feeds。

在blog应用的目录下创建一个新文件命名为feeds.py。添加如下代码:

from django.contrib.syndication.views import Feedfrom django.template.defaultfilters import truncatewordsfrom .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.title        
    def item_description(self, item):
        return truncatewords(item.body, 30)        

首先,我们继承了syndication框架的Feed类创建了一个子类。其中的title,link,description属性各自对应RSS中的<title>,<link>,<description>元素。

items()方法返回包含在feed中的对象。我们只给这个feed取回最新五个已发布的帖子。item_title()item_description()方法接受items()返回的每个对象然后返回每个item各自的标题和描述信息。我们使用内置的truncatewords模板过滤器(template filter)构建帖子的描述信息并只保留最前面的30个单词。

现在,编辑blog应用下的urls.py文件,导入你刚创建的LatestPostsFeed,在新的URL模式(pattern)中实例化feed:

from .feeds import LatestPostsFeed

urlpatterns = [    # ...
    url(r'^feed/$', LatestPostsFeed(), name='post_feed'),
]

在浏览器中转到 http://127.0.0.1:8000/blog/feed/ 。你会看到最新的5个blog帖子的RSS feedincluding:

<?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,通过用户友好的接口你能看到你的feed。

最后一步是在blog的侧边栏(sitebar)添加一个feed订阅(subscription)链接。打开blog/base.html模板(template),在侧边栏(sitebar)的div中的帖子总数下添加如下代码:

<p><a href="{% url "blog:post_feed" %}">Subscribe to my RSS feed</a></p>

现在,在浏览器中打开 http://127.0.0.1:8000/blog/ 看下侧边栏(sitebar)。这个新链接将会带你去blog的feed:


以上是关于django搭建个人博客的主要内容,如果未能解决你的问题,请参考以下文章

Django搭建个人博客平台2---创建一个Django项目和项目梳理

Django搭建个人博客平台2---创建一个Django项目和项目梳理

Django搭建个人博客平台6---前端templates模板index页

Django搭建个人博客平台6---前端templates模板index页

Django搭建个人博客平台3---博客表结构设计和markdown编辑器

Django搭建个人博客平台3---博客表结构设计和markdown编辑器