snippets.rst 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. .. _snippets:
  2. Snippets
  3. ========
  4. Snippets are pieces of content which do not necessitate a full webpage to render. They could be used for making secondary content, such as headers, footers, and sidebars, editable in the Wagtail admin. Snippets are Django models which do not inherit the ``Page`` class and are thus not organized into the Wagtail tree. However, they can still be made editable by assigning panels and identifying the model as a snippet with the ``register_snippet`` class decorator.
  5. Snippets lack many of the features of pages, such as being orderable in the Wagtail admin or having a defined URL. Decide carefully if the content type you would want to build into a snippet might be more suited to a page.
  6. Snippet Models
  7. --------------
  8. Here's an example snippet model:
  9. .. code-block:: python
  10. from django.db import models
  11. from wagtail.admin.edit_handlers import FieldPanel
  12. from wagtail.snippets.models import register_snippet
  13. ...
  14. @register_snippet
  15. class Advert(models.Model):
  16. url = models.URLField(null=True, blank=True)
  17. text = models.CharField(max_length=255)
  18. panels = [
  19. FieldPanel('url'),
  20. FieldPanel('text'),
  21. ]
  22. def __str__(self):
  23. return self.text
  24. The ``Advert`` model uses the basic Django model class and defines two properties: text and URL. The editing interface is very close to that provided for ``Page``-derived models, with fields assigned in the ``panels`` property. Snippets do not use multiple tabs of fields, nor do they provide the "save as draft" or "submit for moderation" features.
  25. ``@register_snippet`` tells Wagtail to treat the model as a snippet. The ``panels`` list defines the fields to show on the snippet editing page. It's also important to provide a string representation of the class through ``def __str__(self):`` so that the snippet objects make sense when listed in the Wagtail admin.
  26. Including Snippets in Template Tags
  27. -----------------------------------
  28. The simplest way to make your snippets available to templates is with a template tag. This is mostly done with vanilla Django, so perhaps reviewing Django's documentation for :doc:`django custom template tags <howto/custom-template-tags>` will be more helpful. We'll go over the basics, though, and point out any considerations to make for Wagtail.
  29. First, add a new python file to a ``templatetags`` folder within your app - for example, ``myproject/demo/templatetags/demo_tags.py``. We'll need to load some Django modules and our app's models, and ready the ``register`` decorator:
  30. .. code-block:: python
  31. from django import template
  32. from demo.models import Advert
  33. register = template.Library()
  34. ...
  35. # Advert snippets
  36. @register.inclusion_tag('demo/tags/adverts.html', takes_context=True)
  37. def adverts(context):
  38. return {
  39. 'adverts': Advert.objects.all(),
  40. 'request': context['request'],
  41. }
  42. ``@register.inclusion_tag()`` takes two variables: a template and a boolean on whether that template should be passed a request context. It's a good idea to include request contexts in your custom template tags, since some Wagtail-specific template tags like ``pageurl`` need the context to work properly. The template tag function could take arguments and filter the adverts to return a specific instance of the model, but for brevity we'll just use ``Advert.objects.all()``.
  43. Here's what's in the template used by this template tag:
  44. .. code-block:: html+django
  45. {% for advert in adverts %}
  46. <p>
  47. <a href="{{ advert.url }}">
  48. {{ advert.text }}
  49. </a>
  50. </p>
  51. {% endfor %}
  52. Then, in your own page templates, you can include your snippet template tag with:
  53. .. code-block:: html+django
  54. {% load wagtailcore_tags demo_tags %}
  55. ...
  56. {% block content %}
  57. ...
  58. {% adverts %}
  59. {% endblock %}
  60. Binding Pages to Snippets
  61. -------------------------
  62. In the above example, the list of adverts is a fixed list that is displayed via the custom template tag independent of any other content on the page. This might be what you want for a common panel in a sidebar, but, in another scenario, you might wish to display just one specific instance of a snippet on a particular page. This can be accomplished by defining a foreign key to the snippet model within your page model and adding a ``SnippetChooserPanel`` to the page's ``content_panels`` list. For example, if you wanted to display a specific advert on a ``BookPage`` instance:
  63. .. code-block:: python
  64. from wagtail.snippets.edit_handlers import SnippetChooserPanel
  65. # ...
  66. class BookPage(Page):
  67. advert = models.ForeignKey(
  68. 'demo.Advert',
  69. null=True,
  70. blank=True,
  71. on_delete=models.SET_NULL,
  72. related_name='+'
  73. )
  74. content_panels = Page.content_panels + [
  75. SnippetChooserPanel('advert'),
  76. # ...
  77. ]
  78. The snippet could then be accessed within your template as ``page.advert``.
  79. To attach multiple adverts to a page, the ``SnippetChooserPanel`` can be placed on an inline child object of ``BookPage`` rather than on ``BookPage`` itself. Here, this child model is named ``BookPageAdvertPlacement`` (so called because there is one such object for each time that an advert is placed on a BookPage):
  80. .. code-block:: python
  81. from django.db import models
  82. from wagtail.core.models import Page, Orderable
  83. from wagtail.snippets.edit_handlers import SnippetChooserPanel
  84. from modelcluster.fields import ParentalKey
  85. ...
  86. class BookPageAdvertPlacement(Orderable, models.Model):
  87. page = ParentalKey('demo.BookPage', on_delete=models.CASCADE, related_name='advert_placements')
  88. advert = models.ForeignKey('demo.Advert', on_delete=models.CASCADE, related_name='+')
  89. class Meta(Orderable.Meta):
  90. verbose_name = "advert placement"
  91. verbose_name_plural = "advert placements"
  92. panels = [
  93. SnippetChooserPanel('advert'),
  94. ]
  95. def __str__(self):
  96. return self.page.title + " -> " + self.advert.text
  97. class BookPage(Page):
  98. ...
  99. content_panels = Page.content_panels + [
  100. InlinePanel('advert_placements', label="Adverts"),
  101. # ...
  102. ]
  103. These child objects are now accessible through the page's ``advert_placements`` property, and from there we can access the linked Advert snippet as ``advert``. In the template for ``BookPage``, we could include the following:
  104. .. code-block:: html+django
  105. {% for advert_placement in page.advert_placements.all %}
  106. <p>
  107. <a href="{{ advert_placement.advert.url }}">
  108. {{ advert_placement.advert.text }}
  109. </a>
  110. </p>
  111. {% endfor %}
  112. .. _wagtailsnippets_making_snippets_searchable:
  113. Making Snippets Searchable
  114. --------------------------
  115. If a snippet model inherits from ``wagtail.search.index.Indexed``, as described in :ref:`wagtailsearch_indexing_models`, Wagtail will automatically add a search box to the chooser interface for that snippet type. For example, the ``Advert`` snippet could be made searchable as follows:
  116. .. code-block:: python
  117. ...
  118. from wagtail.search import index
  119. ...
  120. @register_snippet
  121. class Advert(index.Indexed, models.Model):
  122. url = models.URLField(null=True, blank=True)
  123. text = models.CharField(max_length=255)
  124. panels = [
  125. FieldPanel('url'),
  126. FieldPanel('text'),
  127. ]
  128. search_fields = [
  129. index.SearchField('text', partial_match=True),
  130. ]
  131. Tagging snippets
  132. ----------------
  133. Adding tags to snippets is very similar to adding tags to pages. The only difference is that :class:`taggit.manager.TaggableManager` should be used in the place of :class:`~modelcluster.contrib.taggit.ClusterTaggableManager`.
  134. .. code-block:: python
  135. from modelcluster.fields import ParentalKey
  136. from modelcluster.models import ClusterableModel
  137. from taggit.models import TaggedItemBase
  138. from taggit.managers import TaggableManager
  139. class AdvertTag(TaggedItemBase):
  140. content_object = ParentalKey('demo.Advert', on_delete=models.CASCADE, related_name='tagged_items')
  141. @register_snippet
  142. class Advert(ClusterableModel):
  143. ...
  144. tags = TaggableManager(through=AdvertTag, blank=True)
  145. panels = [
  146. ...
  147. FieldPanel('tags'),
  148. ]
  149. The :ref:`documentation on tagging pages <tagging>` has more information on how to use tags in views.