.. _snippets: 片段 ======== 片段一般是页面中引用的一小段内容。用来动态定义一些内容,例如标题、边栏、脚注等。它可以在 Wagtail 后台管理界面中进行编辑。片段不从 ``Page`` 类继承,而使用 Django 模型类进行定义,所在不在页面树中显示。 片段通过 ``register_snippet`` 类修饰器进行注册,然后可以后台管理的片段子菜单中进行编辑。 片段缺少了页面的很多属性,例如排序、预定义的URL,也没有版本管理及审核流程等,在使用片段时应仔细考虑,有时更适合采用页面形式。一般片段是与其它页面进行组合使用,而不单独呈现。 片段模型 -------------- 下面一个片段模型的例子: .. code-block:: python from django.db import models from wagtail.admin.edit_handlers import FieldPanel from wagtail.snippets.models import register_snippet ... @register_snippet class Advert(models.Model): url = models.URLField(null=True, blank=True) text = models.CharField(max_length=255) panels = [ FieldPanel('url'), FieldPanel('text'), ] def __str__(self): return self.text ``Advert`` 使用 Django 模型类做为基类,定义了两个属性:文本和URL。后台管理界面的编辑界面风格和页面风格类似,需要编辑的字段赋值给 ``panels`` 属性。片段编辑界面没有多个 Tab 页,也没有保存为草稿及提供审核的功能。 ``@register_snippet`` 告诉 Wagtail 将这个模型注册为片段。``panels`` 列表定义了显示在编辑界面上的字段。通过 ``def __str__(self):`` 定义类对象的字符串显示内容也是很重要的,它输出的内容会显示在后台管理片段一览界面上,用来标识片段。 在模板标签中使用片段 ----------------------------------- 在模板中最简单的使用片段方法是使用标签,使用 Django 中定制标签,具体可参考文档 :doc:`django 定制模板标签 ` 。这里简单说明一下过程,并解释和 Wagtail 相关的一些内容。 首先,在应用的 ``templatetags`` 文件夹中创建一个 Python 程序文件,例如 ``myproject/demo/templatetags/demo_tags.py``。 增加相关 Django 模块及我们自定义模型的引用,最后加上 ``register`` 修饰符: .. code-block:: python from django import template from demo.models import Advert register = template.Library() ... # Advert snippets @register.inclusion_tag('demo/tags/adverts.html', takes_context=True) def adverts(context): return { 'adverts': Advert.objects.all(), 'request': context['request'], } ``@register.inclusion_tag()`` 方法有两个参数: 模板以及是否传递上下文。在自定义标签中,最好将模板的上下文传入,一些 Wagtail 专有的模板标签,如 ``pageurl`` 需要上下文才能正常工作。 标签函数可以通过获取参数值后过滤广告列表返回一个模型实例,为了简化起见,这里采用 ``Advert.objects.all()`` 返回全部对象。 下面是 ``demo/tags/adverts.html`` 模板使用自定义标签的示例: .. code-block:: html+django {% for advert in adverts %}

{{ advert.text }}

{% endfor %} 接下来在页面模板中,只需简单的引用片段模板标签即可: .. code-block:: html+django {% load wagtailcore_tags demo_tags %} ... {% block content %} ... {% adverts %} {% endblock %} 将页面绑定到片段 ------------------------- 在上面的例子中,标签返回的广告列表是固定的,在每个页面上都一样。这在通用的边栏可以使用,有时我们需要根据页面内容来关联特定的广告。 这里需要在页面模型中定义外键关系关联到片段,然后在 ``content_panels`` 增加 ``SnippetChooserPanel`` 片段选择器为一个页面选择指定的片段。例如,在 ``BookPage`` 页面实例中指定广告片段: .. code-block:: python from wagtail.snippets.edit_handlers import SnippetChooserPanel # ... class BookPage(Page): advert = models.ForeignKey( 'demo.Advert', null=True, blank=True, on_delete=models.SET_NULL, related_name='+' ) content_panels = Page.content_panels + [ SnippetChooserPanel('advert'), # ... ] 片段可以在模板文件中通过 ``page.advert`` 进行引用。 如果要关联多个片段, ``SnippetChooserPanel`` 可以不直接放在 ``BookPage`` 中,而是放在内嵌子对象中。这里使用 ``BookPageAdvertPlacement`` 子对象 (这样起名的原因是每次将一个广告放入一个图书面面): .. code-block:: python from django.db import models from wagtail.core.models import Page, Orderable from wagtail.snippets.edit_handlers import SnippetChooserPanel from modelcluster.fields import ParentalKey ... class BookPageAdvertPlacement(Orderable, models.Model): page = ParentalKey('demo.BookPage', on_delete=models.CASCADE, related_name='advert_placements') advert = models.ForeignKey('demo.Advert', on_delete=models.CASCADE, related_name='+') class Meta(Orderable.Meta): verbose_name = "advert placement" verbose_name_plural = "advert placements" panels = [ SnippetChooserPanel('advert'), ] def __str__(self): return self.page.title + " -> " + self.advert.text class BookPage(Page): ... content_panels = Page.content_panels + [ InlinePanel('advert_placements', label="Adverts"), # ... ] 这些子对象可以通过页面的 ``advert_placements`` 属性存取,从中可以引用的片段字段 ``advert``。 在 ``BookPage`` 对应的模板中可以包含: .. code-block:: html+django {% for advert_placement in page.advert_placements.all %}

{{ advert_placement.advert.text }}

{% endfor %} .. _wagtailsnippets_making_snippets_searchable: 增加片段搜索功能 -------------------------- 如果片段模型从 ``wagtail.search.index.Indexed`` 继承,参考 :ref:`wagtailsearch_indexing_models` 描述, Wagtail 将为这个片段类型自动增加搜索框。 例如,可搜索的 ``Advert`` 片段可以定义如下: .. code-block:: python ... from wagtail.search import index ... @register_snippet class Advert(index.Indexed, models.Model): url = models.URLField(null=True, blank=True) text = models.CharField(max_length=255) panels = [ FieldPanel('url'), FieldPanel('text'), ] search_fields = [ index.SearchField('text', partial_match=True), ] 为片段打标签 ---------------- 为片段打标签与为页面打标签类似。唯一的区别是使用 :class:`taggit.manager.TaggableManager` 而不是使用 :class:`~modelcluster.contrib.taggit.ClusterTaggableManager`。 .. code-block:: python from modelcluster.fields import ParentalKey from modelcluster.models import ClusterableModel from taggit.models import TaggedItemBase from taggit.managers import TaggableManager class AdvertTag(TaggedItemBase): content_object = ParentalKey('demo.Advert', on_delete=models.CASCADE, related_name='tagged_items') @register_snippet class Advert(ClusterableModel): ... tags = TaggableManager(through=AdvertTag, blank=True) panels = [ ... FieldPanel('tags'), ] 参考 :ref:`页面打标签文档 ` 以获取视图中使用标签的详细说明。