Wagtail建站入门(二)
Wagtail是一款基于Django的CMS开源框架,集成的功能非常多,很适合做定制站点开发。本文是从Wagtail官网翻译的一篇教程(官网教程:Your first Wagtail site),由于原文比较长,我把它分成了三个部分,今天这篇文章是第二部分,主要内容是搭建能够显示博客列表和博客文章的一个简单博客。
如果您还没有看过前面的教程,可以参考:
一个简单的博客应用
现在我们准备创建一个简单的博客应用,首先,运行下面的命令,在我们的项目中创建一个blog应用。
python manage.py startapp blog
在mysite/settings/base.py 的INSTALLED_APPS中添加blog。
博客列表和博客文章(post)
让我们从创建博客列表的页面开始,在blog/models.py中,编辑代码如下:
from wagtail.models import Page
from wagtail.fields import RichTextField
from wagtail.admin.panels import FieldPanel
class BlogIndexPage(Page):
intro = RichTextField(blank=True)
content_panels = Page.content_panels + [
FieldPanel('intro', classname="full")
]
运行
python manage.py makemigrations
python manage.py migrate
因为Model的名称叫BlogIndexPage, 默认的模板名称就是
blog/templates/blog/blog_index_page.html,我们在对应目录创建出这个文件,然后增加如下内容:
{% extends "base.html" %}
{% load wagtailcore_tags %}
{% block body_class %}template-blogindexpage{% endblock %}
{% block content %}
{{ page.title }}
{{ page.intro|richtext }}
{% for post in page.get_children %}
{{ post.title }}
{{ post.specific.intro }}
{{ post.specific.body|richtext }}
{% endfor %}
{% endblock %}
大部分的内容我们应该已经很熟悉了,我们在稍后会解释get_children。注意pageurl标签,这个标签就类似Django的url标签,只是这里需要将Wagtail的page作为参数。
接下来就是创建出对应的页面。进入Wagtail的admin后台,创建BlogIndexPage,注意选择作为Homepage的下级页面, 同时确保在Promote属性页中的slug输入blog,然后发布(publish),正常情况我们可以正常访问
http://127.0.0.1:8000/blog/了。
这时候相信大家已经发现Wagtail与常规Django编程的差别了,那就是目前为止,我们没有用到MTV架构的View。
现在我们需要为Blog文章(BlogPage)创建model和模板,回到blog/models.py:
from django.db import models
from wagtail.models import Page
from wagtail.fields import RichTextField
from wagtail.admin.panels import FieldPanel
from wagtail.search import index
# Keep the definition of BlogIndexPage, and add:
class BlogPage(Page):
date = models.DateField("Post date")
intro = models.CharField(max_length=250)
body = RichTextField(blank=True)
search_fields = Page.search_fields + [
index.SearchField('intro'),
index.SearchField('body'),
]
content_panels = Page.content_panels + [
FieldPanel('date'),
FieldPanel('intro'),
FieldPanel('body', classname="full"),
]
在上面的Model中,我们引入索引(index)使得这个model具有搜索功能,也就是说后面通过扩展首页的搜索功能,就可以按照文章进行搜索了,目前我们只需要将需要搜索的字段通过SearchField加入即可。修改好Model后运行:
python manage.py makemigrations
python manage.py migrate.
接下来创建模板,添加模板文件:
blog/templates/blog/blog_page.html:
{% extends "base.html" %}
{% load wagtailcore_tags %}
{% block body_class %}template-blogpage{% endblock %}
{% block content %}
{{ page.title }}
{{ page.intro }}
{{ page.body|richtext }}
{% endblock %}
注意这里使用Wagtail内置的方法get_parent()来获得当前Page的父节点的链接。
回到Admin管理员界面,我们为BlogIndexPage创建若干个子Page,Page的类型选择“Blog Page”。
Wagtail的Page管理十分强大,你可以在每个Page下面,创建任何类型的Page子页面。
把我们刚才添加的Page全部发布。这时候我们就完成了一个非常简单的Blog系统,通过访问/blog,我们可以看到类似这个界面:
博客文件标题应该可以链接到具体的博客文章页面,在具体的文章页面底部应该有一个返回到列表的链接。
父页面和子页面
在Wagtail中我们大部分的工作都围绕着树形结构这个概念开展,树形结构由节点和树叶组成,在这个例子中,BlogIndexPage就是一个节点,每一个BlogPage 的实例就是树叶。
再看一下blog_index_page.html一个重要的知识点:
{% for post in page.get_children %}
{{ post.title }}
{{ post.specific.intro }}
{{ post.specific.body|richtext }}
{% endfor %}
Wagtail中的每一个Page都可以调用它的父页面或子页面,为什么这里我们还明确要使用post.specific.intro而不直接用post.intro?我们来解释一下:
模板代码中get_children()方法会返回给我们一个Page实例列表,当我们想遍历Page实例中定义的属性的时候,我们是无法知道具体page实例是由哪个类创建的。所以Wagtail提供了specific方法,通过这个方法,可以感知Page实例的类是什么(在这里就是BlogPage),这样就可以按照BlogPage的实例去获得title和intro的值。也就是说我们需要 specific出来具体的类,然后访问实例的属性。这段比较难理解,实在不理解直接照葫芦画瓢先使用即可:)
为了让模板代码更具可读性(不出现specific),也可以使用with标签:
{% for post in page.get_children %}
{% with post=post.specific %}
{{ post.title }}
{{ post.intro }}
{{ post.body|richtext }}
{% endwith %}
{% endfor %}
当我们开始写更多定制化的Wagtail代码时,我们可以查询QuerySet的方法(如下列示)来帮助遍历和定位到需要的页面层次结构(总结说就是想要怎么查都可以)。
# Given a page object 'somepage':
MyModel.objects.descendant_of(somepage)
child_of(page) / not_child_of(somepage)
ancestor_of(somepage) / not_ancestor_of(somepage)
parent_of(somepage) / not_parent_of(somepage)
sibling_of(somepage) / not_sibling_of(somepage)
# ... and ...
somepage.get_children()
somepage.get_ancestors()
somepage.get_descendants()
somepage.get_siblings()
重写(Overriding)Context
目前在我们的Blog列表中有几个问题:
- 博客通常按时间倒序显示内容
- 我们希望确保只显示已发布的内容。
为了完成这些事情,我们需要做的不仅仅是在模板中抓取索引页的子页面。我们希望的是修改模型定义中的查询集。Wagtail通过重写的get_context()方法来改变返回的默认列表。修改BlogIndexPage类如下:
class BlogIndexPage(Page):
intro = RichTextField(blank=True)
def get_context(self, request):
# Update context to include only
published posts, ordered by reverse-chron
context = super().get_context(request)
blogpages = self.get_children().live().order_by('-first_published_at')
context['blogpages'] = blogpages
return context
我们在这里所做的就是检索原始上下文,创建一个自定义查询集,将其添加到检索到的上下文中,然后将修改后的上下文返回到视图中。之后我们还需要将blog_index_page.html中的{% for post in page.get_children %}
修改为{% for post in blogpages %}
现在试着取消发布你的一篇文章——它应该会从博客列表页面中消失。剩下的博客文章也应该是按照最新日期降序排列。