内容块(Snippets)

所谓片段(Snippets),是指小片的、没有充分理由渲染成为一个完整网页的内容。他们可用于次级内容,诸如页面头部、页脚以及侧边栏等的制作,在Wagtail管理界面可进行编辑。片段是一些没有从Page基类进行继承的Django模型,并因此没有组织到Wagtail树中。但他们仍可通过赋予其面板,而成为可编辑的内容,并经由register_snippet类装饰器,将某个模型标识为片段。

片段缺少页面的很多特性,比如在Wagtail管理界面可被排序,或拥有一个定义的URL。要仔细区分打算构建为片段的内容类型,是否更适合构建为一个页面。

片段模型

下面是一个片段模型的示例:

  1. from django.db import models
  2. from wagtail.admin.edit_handlers import FieldPanel
  3. from wagtail.snippets.models import register_snippet
  4. ...
  5. @register_snippet
  6. class Advert(models.Model):
  7. url = models.URLField(null=True, blank=True)
  8. text = models.CharField(max_length=255)
  9. panels = [
  10. FieldPanel('url'),
  11. FieldPanel('text'),
  12. ]
  13. def __str__(self):
  14. return self.text

Advert模型使用了基本的Django模型类,并定义了两个属性:textURL。编辑节目与提供给派生自Page的类非常接近,有着在panels属性中所指派的几个字段。片段不会用到多个分栏的字段,同时也不提供“保存为草稿”或“提交审核”特性。

@register_snippet告诉Wagtail将该模型作为一个片段进行处理。panels清单定义了在该片段的编辑页面上展示的字段。经由def __str__(self): 提供一个表示该类的字符串也很重要,这样做才能在片段于Wagtail管理界面中被列出时,片段对象才有意义。

在模板标签中将片段包含进去

将片段暴露给模板的最简单方式,就是通过使用模板标签。这主要是通过普通的Django实现的,因此最好复习一下Django的django定制模板标签文档,那将更有助益。这里仍将回顾到基础知识,还将指出一些对于Wagtail需要考虑的地方(We’ll go over the basics, though, and point out any considerations to make for Wagtail)。

首先将一个新的Python文件加入到应用中的templatetags文件夹 — 比如myproject/demo/templatetags/demo_tags.py。这里将装入一些Django的模块,以及应用的模型,并准备好register装饰器:

  1. from django import template
  2. from demo.models import Advert
  3. register = template.Library()
  4. ...
  5. # Advert 片段
  6. @register.inclusion_tag('demo/tags/advert.html', takes_context=True)
  7. def adverts(context):
  8. return {
  9. 'adverts': Advert.objects.all(),
  10. 'request': conext['request'],
  11. }

@register.inclusion_tag() 需要两个变量:一个模板与一个该模板是否要传入一个请求上下文的逻辑值。将请求上下文包含在定制模板标签中,是种好的做法,因为某些特定于Wagtail的模板标签,如pageurl,就需要上下文才能正确工作。模板标签函数可取一些参数,并对该adverts进行过滤,以返回一个特定模型,这里因为简要而仅使用了Advert.objects.all()

下面是使用到模板标签所使用的模板:

  1. {% for advert in adverts %}
  2. <p>
  3. <a href="{{ advert.url }}">{{ advert.text }}</a>
  4. </p>
  5. {% endfor %}

随后在页面模板中,据可以这样来将该片段模板标签包含进来了:

{% raw %} {% load wagtailcore_tags demo_tags %}

  1. ...
  2. {% block content %}
  3. ...
  4. {% adverts %}
  5. {% endblock %}

{% endraw %}

将页面绑定到片段

在上述示例中,adverts的清单是一个固定清单,其显示是独立于页面内容的。这种形式对于某个侧边栏中的普通面板可能是预期的效果,但在其他场景下,可能希望对某个页面内容中的特定片段进行引用(this might be what you want for a common panel in a sidebar, say — but in other scenarios you may wish to refer to a particular snippet from within a page’s content)。这可通过在页面模型中定义一个到片段模型的外键,并将一个SnippetChooserPanel到添加到页面的content_panels来实现。比如在打算指定某个advert要出现在BookPage上时:

  1. from wagtail.snippets.edit_handlers import SnippetChooserPanel
  2. # ...
  3. class BookPage(Page):
  4. advert = models.ForeignKey(
  5. 'demo.Advert',
  6. null=True,
  7. blank=True,
  8. on_delete=models.SET_NULL,
  9. related_name='+'
  10. )
  11. content_panels = Page.content_panels + [
  12. SnippetChooserPanel('advert'),
  13. # ...
  14. ]

随后该片段就可以在模板中作为page.advert进行访问了。

要将多个adverts附加到某个页面,就可将SnippetChooserPanel放置在BookPage的某个内联子对象上,而不要在BookPage上。下面的子模型被命名为了BookPageAdvertPlacement(之所以这样命名,是因为对于每次将advert放置在BookPage上时,都有一个这样的对象,here this child model is named BookPageAdvertPlacement(so called because there is on such object for each time that an advert is placed on a BookPage))。

  1. from django.db import models
  2. from wagtail.core.models import Page, Orderable
  3. from wagtail.snippets.edit_handlers import SnippetChooserPanel
  4. from modelcluster.fields import ParentalKey
  5. ...
  6. class BookPageAdvertPlacement(Orderable, models.Model):
  7. page = ParentalKey('demo.BookPage', on_delete=models.CASCADE, related_name='advert_placements')
  8. advert = models.ForeignKey('demo.Advert', on_delete=models.CASCADE, related_name='+')
  9. class Meta:
  10. verbose_name = "广告位"
  11. verbose_name_plural = "广告位"
  12. panels = [
  13. SnippetChooserPanel('advert'),
  14. ]
  15. def __str__(self):
  16. return self.page.title + " -> " + self.advert.text
  17. class BookPage(Page):
  18. ...
  19. content_panels = Page.content_panels + [
  20. InlinePanel('advert_placements', label="广告"),
  21. # ...
  22. ]

现在这些子对象就可经由页面的advert_placements属性访问到了,且从那里可以advert访问到链接的Advert。在BookPage的模板中,可包含以下代码:

  1. {% for advert_placement in page.advert_placements.all %}
  2. <p><a href="{{ advert_placement.advert.url }}">{{ advert_placement.advert.text }}</a></p>
  3. {% endfor %}

令到片段可被搜索

在片段模型继承了对定制模型进行索引所降到的wagtail.search.index.Indexed时,Wagtail将自动把一个搜索框添加到那个片段类型的选择器界面上。比如该Advert片段可像下面这样做成可搜索的:

  1. ...
  2. from wagtail.search import index
  3. ...
  4. @register_snippet
  5. class Advert(index.Indexed, models.Model):
  6. url = models.URLField(null=True, blank=True)
  7. text = models.CharField(max_length=255)
  8. panels = [
  9. FieldPanel('url'),
  10. FieldPanel('text'),
  11. ]
  12. search_fields = [
  13. index.SearchField('text', partial_match=True),
  14. ]

给片段打上标签

将标签添加到片段,与将标签添加到页面非常类似。唯一差别在于应在ClusterTaggableManager处使用taggit.manager.TaggableManager

  1. from modelcluster.fields import ParentalKey
  2. from modelcluster.models import ClusterableModel
  3. from taggit.models import TaggedItemBase
  4. from taggit.managers import TaggableManager
  5. class AvertTag(TaggedItemBase):
  6. content_object = ParentalKey('demo.Advert', on_delete=models.CASCADE, related_name='taggged_items')
  7. @register_snippet
  8. class Advert(ClusterableModel):
  9. ...
  10. tags = TaggableManager(through=AdvertTag, blank=True)
  11. panels = [
  12. ...
  13. FieldPanel('tags'),
  14. ]

关于更多有关在视图中使用标签的知识,请参阅给页面打标签的文档