pages.rst 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. ===========
  2. Page models
  3. ===========
  4. Each page type (a.k.a. content type) in Wagtail is represented by a Django model. All page models must inherit from the :class:`wagtail.wagtailcore.models.Page` class.
  5. As all page types are Django models, you can use any field type that Django provides. See `Model field reference <https://docs.djangoproject.com/en/1.7/ref/models/fields/>`_ for a complete list of field types you can use. Wagtail also provides :class:`~wagtail.wagtailcore.fields.RichTextField` which provides a WYSIWYG editor for editing rich-text content.
  6. .. topic:: Django models
  7. If you're not yet familiar with Django models, have a quick look at the following links to get you started:
  8. * `Creating models <https://docs.djangoproject.com/en/1.7/intro/tutorial01/#creating-models>`_
  9. * `Model syntax <https://docs.djangoproject.com/en/1.7/topics/db/models/>`_
  10. An example Wagtail page model
  11. =============================
  12. This example represents a typical blog post:
  13. .. code-block:: python
  14. from django.db import models
  15. from modelcluster.fields import ParentalKey
  16. from wagtail.wagtailcore.models import Page, Orderable
  17. from wagtail.wagtailcore.fields import RichTextField
  18. from wagtail.wagtailadmin.edit_handlers import FieldPanel, MultiFieldPanel, InlinePanel
  19. from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
  20. from wagtail.wagtailsearch import index
  21. class BlogPage(Page):
  22. # Database fields
  23. body = RichTextField()
  24. date = models.DateField("Post date")
  25. feed_image = models.ForeignKey(
  26. 'wagtailimages.Image',
  27. null=True,
  28. blank=True,
  29. on_delete=models.SET_NULL,
  30. related_name='+'
  31. )
  32. # Search index configuraiton
  33. search_fields = Page.search_fields + (
  34. index.SearchField('body'),
  35. index.FilterField('date'),
  36. )
  37. # Editor panels configuration
  38. content_panels = Page.content_panels + [
  39. FieldPanel('date'),
  40. FieldPanel('body', classname="full"),
  41. InlinePanel('related_links', label="Related links"),
  42. ]
  43. promote_panels = [
  44. MultiFieldPanel(Page.promote_panels, "Common page configuration"),
  45. ImageChooserPanel('feed_image'),
  46. ]
  47. # Parent page / subpage type rules
  48. parent_page_types = ['blog.BlogIndex']
  49. subpage_types = []
  50. class BlogPageRelatedLink(Orderable):
  51. page = ParentalKey(BlogPage, related_name='related_links')
  52. name = models.CharField(max_length=255)
  53. url = models.URLField()
  54. panels = [
  55. FieldPanel('name'),
  56. FieldPanel('url'),
  57. ]
  58. .. tip::
  59. To keep track of ``Page`` models and avoid class name clashes, it can be helpful to suffix model class names with "Page" e.g BlogPage, ListingIndexPage.
  60. Writing page models
  61. ===================
  62. Here we'll describe each section of the above example to help you create your own page models.
  63. Database fields
  64. ---------------
  65. Each Wagtail page type is a Django model, represented in the database as a separate table.
  66. Each page type can have its own set of fields. For example, a news article may have body text and a published date, whereas an event page may need separate fields for venue and start/finish times.
  67. In Wagtail, you can use any Django field class. Most field classes provided by `third party apps <https://code.djangoproject.com/wiki/DjangoResources#Djangoapplicationcomponents>`_ should work as well.
  68. Wagtail also provides a couple of field classes of its own:
  69. - ``RichTextField`` - For rich text content
  70. - ``StreamField`` - A block-based content field (see: :doc:`/topics/streamfield`)
  71. For tagging, Wagtail fully supports `django-taggit <https://django-taggit.readthedocs.org/en/latest/>`_ so we recommend using that.
  72. Search
  73. ------
  74. The ``search_fields`` attribute defines which fields are added to the search index and how they are indexed.
  75. This should be a tuple of ``SearchField`` and ``FilterField`` objects. ``SearchField`` adds a field for full-text search. ``FilterField`` adds a field for filtering the results. A field can be indexed with both ``SearchField`` and ``FilterField`` at the same time (but only one instance of each).
  76. In the above example, we've indexed ``body`` for full-text search and ``date`` for filtering.
  77. The arguments that these field types accept are documented here: :ref:`wagtailsearch_indexing_fields`
  78. Editor panels
  79. -------------
  80. There are a few attributes for defining how the page's fields will be arranged in the page editor interface:
  81. - ``content_panels`` - For content, such as main body text
  82. - ``promote_panels`` - For metadata, such as tags, thumbnail image and SEO title
  83. - ``settings_panels`` - For settings, such as publish date
  84. Each of these attributes is set to a list of ``EditHandler`` objects, which defines which fields appear on which tabs and how they are structured on each tab.
  85. Here's a summary of the ``EditHandler`` classes that Wagtail provides out of the box. See :doc:`/reference/pages/panels` for full descriptions.
  86. **Basic**
  87. These allow editing of model fields, the ``FieldPanel`` class will choose the correct widget based on the type of the field. ``StreamField`` fields need to use a specialised panel class.
  88. - :class:`~wagtail.wagtailadmin.edit_handlers.FieldPanel`
  89. - :class:`~wagtail.wagtailadmin.edit_handlers.StreamFieldPanel`
  90. **Structural**
  91. These are used for structuring fields in the interface.
  92. - :class:`~wagtail.wagtailadmin.edit_handlers.MultiFieldPanel` - For grouping similar fields together
  93. - :class:`~wagtail.wagtailadmin.edit_handlers.InlinePanel` - For inlining child models
  94. - :class:`~wagtail.wagtailadmin.edit_handlers.FieldRowPanel` - For organising multiple fields into a single row
  95. **Chooser**
  96. ``ForeignKey`` fields to certain models can use one of the below ``ChooserPanel`` classes. These add a nice modal-based chooser interface (and the image/document choosers also allow uploading new files without leaving the page editor).
  97. - :class:`~wagtail.wagtailadmin.edit_handlers.PageChooserPanel`
  98. - :class:`~wagtail.wagtailimages.edit_handlers.ImageChooserPanel`
  99. - :class:`~wagtail.wagtaildocs.edit_handlers.DocumentChooserPanel`
  100. - :class:`~wagtail.wagtailsnippets.edit_handlers.SnippetChooserPanel`
  101. .. note::
  102. In order to use one of these choosers, the model being linked to must either be a page, image, document or snippet.
  103. To link to any other model type, you should use ``FieldPanel``, which will create a dropdown box.
  104. Customising the page editor interface
  105. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  106. The page editor can be customised further. See :doc:`/advanced_topics/customisation/page_editing_interface`.
  107. Parent page / subpage type rules
  108. --------------------------------
  109. These two attributes allow you to control where page types may be used in your site. It allows you to define rules like "blog entries may only be created under a blog index".
  110. Both take a list of model classes or model names. Model names are of the format ``app_label.ModelName``. If the ``app_label`` is omitted, the same app is assumed.
  111. - ``parent_page_types`` limits which page types this type can be created under
  112. - ``subpage_types`` limits which page types can be created under this type
  113. By default, any page type can be created under any page type and it is not necessary to set these attributes if that's the desired behaviour.
  114. Setting ``parent_page_types`` to an empty list is a good way of preventing a particular page type from being created in the editor interface.
  115. Template rendering
  116. ==================
  117. Each page model can be given a HTML template which is rendered when a user browses to a page on the site frontend. This is the simplest and most common way to get Wagtail content to end users (but not the only way).
  118. Adding a template for a page model
  119. ----------------------------------
  120. Wagtail automatically chooses a name for the template based on the app label and model class name.
  121. Format: ``<app_label>/<model_name (snake cased)>.html``
  122. For example, the template for the above blog page will be: ``blog/blog_page.html``
  123. You just need to create a template in a location where it can be accessed with this name.
  124. Template context
  125. ----------------
  126. Wagtail renders templates with the ``page`` variable bound to the page instance being rendered. Use this to access the content of the page. For example, to get the title of the current page, do ``{{ page.title }}``. All variables provided by `context processors <https://docs.djangoproject.com/en/1.8/ref/templates/api/#subclassing-context-requestcontext>`_ are also available.
  127. Customising template context
  128. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  129. All pages have a ``get_context`` method that is called whenever the template is rendered and returns a dictionary of variables to bind into the template.
  130. To add more variables to the template context, you can override this method:
  131. .. code-block:: python
  132. class BlogIndexPage(Page):
  133. ...
  134. def get_context(self, request):
  135. context = super(BlogIndexPage, self).get_context(request)
  136. # Add extra variables and return the updated context
  137. context['blog_entries'] = BlogPage.objects.child_of(self).live()
  138. return context
  139. The variables can then be used in the template:
  140. .. code-block:: HTML+Django
  141. {{ page.title }}
  142. {% for entry in blog_entries %}
  143. {{ entry.title }}
  144. {% endfor %}
  145. Changing the template
  146. ---------------------
  147. Set the ``template`` attribute on the class to use a different template file:
  148. .. code-block:: python
  149. class BlogPage(Page):
  150. ...
  151. template = 'other_template.html'
  152. Dynamically choosing the template
  153. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  154. The template can be changed on a per-instance basis by defining a ``get_template`` method on the page class. This method is called every time the page is rendered:
  155. .. code-block:: python
  156. class BlogPage(Page):
  157. ...
  158. use_other_template = models.BooleanField()
  159. def get_template(self, request):
  160. if self.use_other_template:
  161. return 'blog/other_blog_page.html'
  162. return 'blog/blog_page.html'
  163. In this example, pages that have the ``use_other_template`` boolean field set will use the ``other_blog_page.html`` template. All other pages will use the default ``blog/blog_page.html``.
  164. More control over page rendering
  165. --------------------------------
  166. All page classes have a ``serve()`` method, that internally calls the ``get_context`` and ``get_template`` methods and renders the template. This method is similar to a Django view function, taking a Django ``Request`` object and returning a Django ``Response`` object.
  167. This method can also be overridden for complete control over page rendering.
  168. For example, here's a way you could make a page respond with a JSON representation of itself:
  169. .. code-block:: python
  170. from django.http import JsonResponse
  171. class BlogPage(Page):
  172. ...
  173. def serve(self, request):
  174. return JsonResponse({
  175. 'title': self.title,
  176. 'body': self.body,
  177. 'date': self.date,
  178. # Resizes the image to 300px width and gets a URL to it
  179. 'feed_image': self.feed_image.get_rendition('width-300').url,
  180. })
  181. Inline models
  182. =============
  183. Wagtail can nest the content of other models within the page. This is useful for creating repeated fields, such as related links or items to display in a carousel. Inline model content is also versioned with the rest of the page content.
  184. Each inline model requires the following:
  185. - It must inherit from :class:`wagtail.wagtailcore.models.Orderable`
  186. - It must have a ``ParentalKey`` to the parent model
  187. .. note:: django-modelcluster and ParentalKey
  188. The model inlining feature is provided by `django-modelcluster <https://github.com/torchbox/django-modelcluster>`_ and the ``ParentalKey`` field type must be imported from there:
  189. .. code-block:: python
  190. from modelcluster.fields import ParentalKey
  191. ``ParentalKey`` is a subclass of Django's ``ForeignKey``, and takes the same arguments.
  192. For example, the following inline model can be used to add related links (a list of name, url pairs) to the ``BlogPage`` model:
  193. .. code-block:: python
  194. from django.db import models
  195. from modelcluster.fields import ParentalKey
  196. from wagtail.wagtailcore.models import Orderable
  197. class BlogPageRelatedLink(Orderable):
  198. page = ParentalKey(BlogPage, related_name='related_links')
  199. name = models.CharField(max_length=255)
  200. url = models.URLField()
  201. panels = [
  202. FieldPanel('name'),
  203. FieldPanel('url'),
  204. ]
  205. To add this to the admin interface, use the :class:`~wagtail.wagtailadmin.edit_handlers.InlinePanel` edit panel class:
  206. .. code-block:: python
  207. content_panels = [
  208. ...
  209. InlinePanel('related_links', label="Related links"),
  210. ]
  211. The first argument must match the value of the ``related_name`` attribute of the ``ParentalKey``.
  212. Working with pages
  213. ==================
  214. Wagtail uses Django's `multi-table inheritance <https://docs.djangoproject.com/en/1.8/topics/db/models/#multi-table-inheritance>`_ feature to allow multiple page models to be used in the same tree.
  215. Each page is added to both Wagtail's builtin :class:`~wagtail.wagtailcore.models.Page` model as well as its user-defined model (such as the ``BlogPage`` model created earlier).
  216. Pages can exist in Python code in two forms, an instance of ``Page`` or an instance of the page model.
  217. When working with multiple page types together, you will typically use instances of Wagtail's :class:`~wagtail.wagtailcore.models.Page` model, which doesn't give you access to any fields specific to their type.
  218. .. code-block:: python
  219. # Get all pages in the database
  220. >>> from wagtail.wagtailcore.models import Page
  221. >>> Page.objects.all()
  222. [<Page: Homepage>, <Page: About us>, <Page: Blog>, <Page: A Blog post>, <Page: Another Blog post>]
  223. When working with a single page type, you can work with instances of the user-defined model that gives access to all the fields available in ``Page`` and any user defined fields for that type.
  224. .. code-block:: python
  225. # Get all blog entries in the database
  226. >>> BlogPage.objects.all()
  227. [<BlogPage: A Blog post>, <BlogPage: Another Blog post>]
  228. You can convert a ``Page`` object to a specific object using the ``.specific`` property (this may cause an additional database lookup).
  229. .. code-block:: python
  230. >>> page = Page.objects.get(title="A Blog post")
  231. >>> page
  232. <Page: A Blog post>
  233. # Note: the blog post is an instance of Page so we cannot access body, date or feed_image
  234. >>> page.specific
  235. <BlogPage: A Blog post>
  236. Tips
  237. ====
  238. Friendly model names
  239. --------------------
  240. You can make your model names more friendly to users of Wagtail by using Django's internal ``Meta`` class with a ``verbose_name``, e.g.:
  241. .. code-block:: python
  242. class HomePage(Page):
  243. ...
  244. class Meta:
  245. verbose_name = "homepage"
  246. When users are given a choice of pages to create, the list of page types is generated by splitting your model names on each of their capital letters. Thus a ``HomePage`` model would be named "Home Page" which is a little clumsy. ``verbose_name`` as in the example above, would change this to read "Homepage" which is slightly more conventional.
  247. Page QuerySet ordering
  248. ----------------------
  249. ``Page``-derived models *cannot* be given a default ordering by using the standard Django approach of adding an ``ordering`` attribute to the internal ``Meta`` class.
  250. .. code-block:: python
  251. class NewsItemPage(Page):
  252. publication_date = models.DateField()
  253. ...
  254. class Meta:
  255. ordering = ('-publication_date', ) # will not work
  256. This is because ``Page`` enforces ordering QuerySets by path. Instead you must apply the ordering explicitly when you construct a QuerySet:
  257. .. code-block:: python
  258. news_items = NewsItemPage.objects.live().order_by('-publication_date')
  259. Page custom managers
  260. --------------------
  261. ``Page`` enforces its own 'objects' manager in its ``__init__`` method, so you cannot add a custom manager at the 'objects' attribute.
  262. .. code-block:: python
  263. class EventPageQuerySet(PageQuerySet):
  264. def future(self):
  265. return self.filter(
  266. start_date__gte=timezone.localtime(timezone.now()).date()
  267. )
  268. class EventPage(Page):
  269. start_date = models.DateField()
  270. objects = EventPageQuerySet.as_manager() # will not work
  271. To use a custom manager you must choose a different attribute name. Make sure to subclass ``wagtail.wagtailcore.models.PageManager``.
  272. .. code-block:: python
  273. from django.db import models
  274. from django.utils import timezone
  275. from wagtail.wagtailcore.models import Page, PageManager
  276. class FutureEventPageManager(PageManager):
  277. def get_queryset(self):
  278. return super().get_queryset().filter(
  279. start_date__gte=timezone.localtime(timezone.now()).date()
  280. )
  281. class EventPage(Page):
  282. start_date = models.DateField()
  283. future_events = FutureEventPageManager()
  284. Then you can use ``EventPage.future_events`` in the manner you might expect.