Wagtail中的每种页面类型(也称为内容类型)都由Django模型表示。所有页面模型都必须继承自 wagtail.models.Page 类。
正如所有页面类型都是Django模型一样,您可以使用Django提供的任何字段类型。请参阅Django模型字段参考获取可用字段类型的完整列表。此外,Wagtail还提供了 wagtail.fields.RichTextField,该字段提供了一个所见即所得的编辑器,用于编辑富文本内容。
一个页面的数据模型包括如下基本内容
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.models import Page, Orderable
from wagtail.fields import RichTextField
from wagtail.admin.panels import FieldPanel, MultiFieldPanel, InlinePanel
from wagtail.search import index
class BlogPage(Page):
# 数据库字段
body = RichTextField()
date = models.DateField("Post date")
feed_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
# 搜索索引配置,body和date可被搜索
search_fields = Page.search_fields + [
index.SearchField('body'),
index.FilterField('date'),
]
# 管理界面编辑面板配置
content_panels = Page.content_panels + [
FieldPanel('date'),
FieldPanel('body'),
InlinePanel('related_links', heading="Related links", label="Related link"),
]
promote_panels = [
MultiFieldPanel(Page.promote_panels, "Common page configuration"),
FieldPanel('feed_image'),
]
# 父页面 / 子页面类型规则
parent_page_types = ['blog.BlogIndex']
subpage_types = []
class BlogPageRelatedLink(Orderable):
page = ParentalKey(BlogPage, on_delete=models.CASCADE, related_name='related_links')
name = models.CharField(max_length=255)
url = models.URLField()
panels = [
FieldPanel('name'),
FieldPanel('url'),
]
包括以下几个部分
每个 Wagtail页面是一个Django数据模型,在数据库中表示一个独立的表。
Wagtail 也提供了一些它自己的类:
RichTextField - For rich text content
StreamField - A block-based content field (see: Freeform page content using StreamField)
对于标签tag Wagtail 完全支持django的标签系统。
搜索字段定义了哪些哪些字段被添加到了搜索索引,是如何被索引的。
search_fields = Page.search_fields + [
index.SearchField('body'),
index.FilterField('date'),
]
定义了页面的字段是如何通过面板被展示在编辑接口的。
content_panels - 用于内容展示,如,文本等
promote_panels - 用于 metadata, 比如 tags, thumbnail image and SEO title
settings_panels - 用户设置, 比如发布日期
又分为基础的和结构型的
编辑数据字段,字段面板会选择合适的wideget来适配这些字段。比如富文本会选择富文本小组件。图片选择小组件选择一个外键指向一个图片模型。
FieldPanel
PageChooserPanel
用于结构化字段
MultiFieldPanel
InlinePanel
FieldRowPanel
这两个属性允许您控制页面类型在站点中的使用位置。它们允许您定义诸如“博客条目只能在博客索引下创建”之类的规则。
parent_page_types 和 subpage_types 都接受模型类或模型名称的列表。模型名称的格式为 app_label.ModelName。如果省略了 app_label,则假定为相同的应用程序。
parent_page_types 限制了可以在此类型下创建哪些页面类型。
subpage_types 限制了可以在此类型下创建哪些页面类型。
默认情况下,任何页面类型都可以在任何页面类型下创建,如果这是期望的行为,则不需要设置这些属性。
将 parent_page_types 设置为空列表是防止特定页面类型在编辑界面中被创建的一种好方法。
检索页面URL的最常见方法是使用 {% pageurl %} 或 {% fullpageurl %} 模板标签。由于它是从模板中调用的,这些标签会自动包含下面提到的优化。
页面模型还包括一些用于覆盖或访问页面URL的低级方法。
Page.get_url_parts(request) 方法通常不会直接调用,但可以被覆盖以为给定的页面模型定义自定义的URL路由。它应该返回一个元组 (site_id, root_url, page_path),这些由 get_url 和 get_full_url(见下文)用于构建给定类型的页面URL。
在覆盖 get_url_parts() 时,应该接受 *args, **kwargs:
def get_url_parts(self, *args, **kwargs):
并在调用 super 上的 get_url_parts 时传递它们(如果适用),例如:
super().get_url_parts(*args, **kwargs)
虽然您可以仅传递 request 关键字参数,但将所有参数原样传递确保了对这些方法签名的任何未来更改的兼容性。
有关更多信息,请参阅 wagtail.models.Page.get_url_parts()。
您可以在需要页面URL时调用 Page.get_url(request) 方法。如果它确定页面在当前站点上(通过请求中的主机名),它将默认返回本地URL(不包括协议或域);否则,它将返回包括协议和域的完整URL。在可能的情况下,应包括可选的 request 参数,以启用每个请求的站点级URL信息的缓存,并促进本地URL的生成。
get_url(request) 的一个常见用例是在项目中包含用于生成导航菜单的任何自定义模板标签中调用。在编写这样的自定义模板标签时,请确保它包括 takes_context=True 并使用 context.get(‘request’) 来安全地传递请求,或者如果上下文中不存在请求,则传递 None。
有关更多信息,请参阅 wagtail.models.Page.get_url()。
要检索完整的URL(包括协议和域),请使用 Page.get_full_url(request)。在可能的情况下,应包括可选的 request 参数,以启用每个请求的站点级URL信息的缓存。
有关更多信息,请参阅 wagtail.models.Page.get_full_url()。
每个页面模型都可以被分配一个 HTML 模板,当用户在站点前端浏览到页面时,该模板将被呈现。这是将 Wagtail 内容传递给最终用户的最简单和最常见的方式之一(但不是唯一的方式)。
Wagtail会根据应用标签和模型类名自动选择模板的名称。
格式: <app_label>/<model_name(蛇形命名)>.html
例如,上述博客页面的模板将是: blog/blog_page.html
你只需在可以使用这个名称访问的位置创建一个模板。
Wagtail通过将页面变量绑定到正在渲染的页面实例来呈现模板。使用这个变量来访问页面的内容。例如,要获取当前页面的标题,请使用 {{ page.title }}。上下文处理器提供的所有变量也都可用。
所有页面都有一个 get_context 方法,每当模板被渲染时都会调用该方法,并返回一个要绑定到模板中的变量字典。
要向模板上下文添加更多变量,您可以覆盖此方法:
class BlogIndexPage(Page):
...
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
# Add extra variables and return the updated context
context['blog_entries'] = BlogPage.objects.child_of(self).live()
return context
然后,这些变量可以在模板中使用:
{{ page.title }}
{% for entry in blog_entries %}
{{ entry.title }}
{% endfor %}
将模板属性设置在类上,以使用不同的模板文件:
class BlogPage(Page):
...
template = 'other_template.html'
可以通过在页面类上定义 get_template 方法,以在每个实例基础上动态更改模板。该方法在每次渲染页面时都会被调用:
class BlogPage(Page):
...
use_other_template = models.BooleanField()
def get_template(self, request, *args, **kwargs):
if self.use_other_template:
return 'blog/other_blog_page.html'
return 'blog/blog_page.html'
在这个例子中,拥有设置了 use_other_template 布尔字段的页面将使用 blog/other_blog_page.html 模板。所有其他页面将使用默认的 blog/blog_page.html 模板。
如果您想要向页面添加 AJAX 功能,例如在页面上直接更新而不是触发完整的页面重新加载的分页列表,您可以设置 ajax_template 属性来指定在通过 AJAX 调用请求页面时使用的替代模板(由 X-Requested-With: XMLHttpRequest HTTP 头指示):
class BlogPage(Page):
...
ajax_template = 'other_template_fragment.html'
template = 'other_template.html'
对页面渲染的更多控制
所有页面类都有一个 serve() 方法,内部调用 get_context 和 get_template 方法,并呈现模板。这个方法类似于一个 Django 视图函数,接受一个 Django Request 对象,并返回一个 Django Response 对象。
这个方法也可以被覆盖,以完全控制页面的渲染。
例如,下面是使页面以其自身的 JSON 表示响应的一种方法:
from django.http import JsonResponse
class BlogPage(Page):
...
def serve(self, request):
return JsonResponse({
'title': self.title,
'body': self.body,
'date': self.date,
# Resizes the image to 300px width and gets a URL to it
'feed_image': self.feed_image.get_rendition('width-300').url,
})
Wagtail 允许在页面内嵌套其他模型。这对于创建重复字段,如相关链接或在轮播中显示的项目,非常有用。内联模型的内容也与页面的其余部分一起进行版本控制。
每个内联模型都需要以下内容:
必须继承自 wagtail.models.Orderable。
必须具有到父模型的 ParentalKey。
模型内联功能由 django-modelcluster 提供,ParentalKey 字段类型必须从该模块导入:
from modelcluster.fields import ParentalKey
ParentalKey 是 Django 的 ForeignKey 的子类,并接受相同的参数。
例如,以下内联模型可用于向 BlogPage 模型添加相关链接(名称、URL 对的列表):
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.models import Orderable
class BlogPageRelatedLink(Orderable):
page = ParentalKey(BlogPage, on_delete=models.CASCADE, related_name='related_links')
name = models.CharField(max_length=255)
url = models.URLField()
panels = [
FieldPanel('name'),
FieldPanel('url'),
]
要将这个添加到管理员界面中,请使用 InlinePanel 编辑面板类:
content_panels = [
...
InlinePanel('related_links', label="Related links"),
]
第一个参数必须与 ParentalKey 的 related_name 属性的值匹配。有关 InlinePanel 接受的参数的简要描述,请参阅 InlinePanel。
在上面的例子中,相关链接被定义为 BlogPage 页面类型上的子对象。通常,相同类型的内联子对象将出现在多个页面类型上,在这些情况下,重复整个模型定义是不可取的。可以通过将共同的字段重构为抽象模型来避免这种情况:
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.models import Orderable
# The abstract model for related links, complete with panels
class RelatedLink(models.Model):
name = models.CharField(max_length=255)
url = models.URLField()
panels = [
FieldPanel('name'),
FieldPanel('url'),
]
class Meta:
abstract = True
# The real model which extends the abstract model with a ParentalKey relation back to the page model.
# This can be repeated for each page type where the relation is to be added
# (for example, NewsPageRelatedLink, PublicationPageRelatedLink and so on).
class BlogPageRelatedLink(Orderable,RelatedLink):
page = ParentalKey(BlogPage, on_delete=models.CASCADE, related_name='related_links')
或者,如果 RelatedLink 将出现在您项目中定义的大量页面类型上,将一个单独的 RelatedLink 模型设置为指向基础的 wagtailcore.Page 模型可能更合适:
class RelatedLink(Orderable):
page = ParentalKey("wagtailcore.Page", on_delete=models.CASCADE, related_name='related_links')
name = models.CharField(max_length=255)
url = models.URLField()
panels = [
FieldPanel('name'),
FieldPanel('url'),
]
然后,related_links 将作为跨所有页面类型的关联可用,尽管它仍然只能在包含了 InlinePanel 在其面板定义中的页面类型上进行编辑。对于其他页面类型,相关链接集将保持为空。
Wagtail 使用 Django 的多表继承功能,允许在同一树中使用多个页面模型。
每个页面都被添加到 Wagtail 内置的 Page 模型和用户定义的模型(例如之前创建的 BlogPage 模型)。
页面可以以两种形式存在于 Python 代码中,即 Page 的实例或页面模型的实例。
在处理多个页面类型时,通常会使用 Wagtail 的 Page 模型的实例,这不会让你访问任何特定于其类型的字段。
# Get all pages in the database
>>> from wagtail.models import Page
>>> Page.objects.all()
[<Page: Homepage>, <Page: About us>, <Page: Blog>, <Page: A Blog post>, <Page: Another Blog post>]
在处理单个页面类型时,可以使用用户定义模型的实例。这样可以访问 Page 中的所有字段,以及该类型的任何用户定义字段。
# Get all blog entries in the database
>>> BlogPage.objects.all()
[<BlogPage: A Blog post>, <BlogPage: Another Blog post>]
可以使用 .specific 属性将 Page 对象转换为其更具体的用户定义等效项。这可能会导致额外的数据库查找。
>>> page = Page.objects.get(title="A Blog post")
>>> page
<Page: A Blog post>
# Note: the blog post is an instance of Page so we cannot access body, date or feed_image
>>> page.specific
<BlogPage: A Blog post>