panels.rst 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. .. _editing-api:
  2. Panel types
  3. ===========
  4. Built-in Fields and Choosers
  5. ----------------------------
  6. Django's field types are automatically recognised and provided with an appropriate widget for input. Just define that field the normal Django way and pass the field name into :class:`~wagtail.admin.edit_handlers.FieldPanel` when defining your panels. Wagtail will take care of the rest.
  7. Here are some Wagtail-specific types that you might include as fields in your models.
  8. .. module:: wagtail.admin.edit_handlers
  9. FieldPanel
  10. ~~~~~~~~~~
  11. .. class:: FieldPanel(field_name, classname=None, widget=None, heading='')
  12. This is the panel used for basic Django field types.
  13. .. attribute:: FieldPanel.field_name
  14. This is the name of the class property used in your model definition.
  15. .. attribute:: FieldPanel.classname
  16. This is a string of optional CSS classes given to the panel which are used in formatting and scripted interactivity. By default, panels are formatted as inset fields.
  17. The CSS class ``full`` can be used to format the panel so it covers the full width of the Wagtail page editor.
  18. The CSS class ``title`` can be used to give the field a larger text size, suitable for representing page titles and section headings.
  19. .. attribute:: FieldPanel.widget (optional)
  20. This parameter allows you to specify a :doc:`Django form widget <django:ref/forms/widgets>` to use instead of the default widget for this field type.
  21. .. attribute:: FieldPanel.heading (optional)
  22. This allows you to override the heading for the panel, which will otherwise be set automatically using the form field's label (taken in turn from a model field's ``verbose_name``).
  23. StreamFieldPanel
  24. ~~~~~~~~~~~~~~~~
  25. .. class:: StreamFieldPanel(field_name, classname=None, widget=None)
  26. This is the panel used for Wagtail's StreamField type (see :ref:`streamfield`).
  27. .. attribute:: FieldPanel.field_name
  28. This is the name of the class property used in your model definition.
  29. .. attribute:: FieldPanel.classname (optional)
  30. This is a string of optional CSS classes given to the panel which are used in formatting and scripted interactivity. By default, panels are formatted as inset fields.
  31. The CSS class ``full`` can be used to format the panel so it covers the full width of the Wagtail page editor.
  32. MultiFieldPanel
  33. ~~~~~~~~~~~~~~~
  34. .. class:: MultiFieldPanel(children, heading="", classname=None)
  35. This panel condenses several :class:`~wagtail.admin.edit_handlers.FieldPanel` s or choosers, from a ``list`` or ``tuple``, under a single ``heading`` string.
  36. .. attribute:: MultiFieldPanel.children
  37. A ``list`` or ``tuple`` of child panels
  38. .. attribute:: MultiFieldPanel.heading
  39. A heading for the fields
  40. .. topic:: Collapsing MultiFieldPanels to save space
  41. By default, ``MultiFieldPanel`` s are expanded and not collapsible. Adding ``collapsible`` to ``classname`` will enable the collapse control. Adding both ``collapsible`` and ``collapsed`` to the ``classname`` parameter will load the editor page with the ``MultiFieldPanel`` collapsed under its heading.
  42. .. code-block:: python
  43. content_panels = [
  44. MultiFieldPanel(
  45. [
  46. ImageChooserPanel('cover'),
  47. DocumentChooserPanel('book_file'),
  48. PageChooserPanel('publisher'),
  49. ],
  50. heading="Collection of Book Fields",
  51. classname="collapsible collapsed"
  52. ),
  53. ]
  54. InlinePanel
  55. ~~~~~~~~~~~
  56. .. class:: InlinePanel(relation_name, panels=None, classname='', heading='', label='', help_text='', min_num=None, max_num=None)
  57. This panel allows for the creation of a "cluster" of related objects over a join to a separate model, such as a list of related links or slides to an image carousel.
  58. This is a powerful but complex feature which will take some space to cover, so we'll skip over it for now. For a full explanation on the usage of ``InlinePanel``, see :ref:`inline_panels`.
  59. FieldRowPanel
  60. ~~~~~~~~~~~~~
  61. .. class:: FieldRowPanel(children, classname=None)
  62. This panel creates a columnar layout in the editing interface, where each of the child Panels appears alongside each other rather than below.
  63. Use of FieldRowPanel particularly helps reduce the "snow-blindness" effect of seeing so many fields on the page, for complex models. It also improves the perceived association between fields of a similar nature. For example if you created a model representing an "Event" which had a starting date and ending date, it may be intuitive to find the start and end date on the same "row".
  64. By default, the panel is divided into equal-width columns, but this can be overridden by adding ``col*`` class names to each of the child Panels of the FieldRowPanel. The Wagtail editing interface is laid out using a grid system, in which the maximum width of the editor is 12 columns. Classes ``col1``-``col12`` can be applied to each child of a FieldRowPanel. The class ``col3`` will ensure that field appears 3 columns wide or a quarter the width. ``col4`` would cause the field to be 4 columns wide, or a third the width.
  65. .. attribute:: FieldRowPanel.children
  66. A ``list`` or ``tuple`` of child panels to display on the row
  67. .. attribute:: FieldRowPanel.classname
  68. A class to apply to the FieldRowPanel as a whole
  69. HelpPanel
  70. ~~~~~~~~~
  71. .. class:: HelpPanel(content='', template='wagtailadmin/edit_handlers/help_panel.html', heading='', classname='')
  72. .. attribute:: HelpPanel.content
  73. HTML string that gets displayed in the panel.
  74. .. attribute:: HelpPanel.template
  75. Path to a template rendering the full panel HTML.
  76. .. attribute:: HelpPanel.heading
  77. A heading for the help content.
  78. .. attribute:: HelpPanel.classname
  79. String of CSS classes given to the panel which are used in formatting and scripted interactivity.
  80. PageChooserPanel
  81. ~~~~~~~~~~~~~~~~
  82. .. class:: PageChooserPanel(field_name, page_type=None, can_choose_root=False)
  83. You can explicitly link :class:`~wagtail.core.models.Page`-derived models together using the :class:`~wagtail.core.models.Page` model and ``PageChooserPanel``.
  84. .. code-block:: python
  85. from wagtail.core.models import Page
  86. from wagtail.admin.edit_handlers import PageChooserPanel
  87. class BookPage(Page):
  88. related_page = models.ForeignKey(
  89. 'wagtailcore.Page',
  90. null=True,
  91. blank=True,
  92. on_delete=models.SET_NULL,
  93. related_name='+',
  94. )
  95. content_panels = Page.content_panels + [
  96. PageChooserPanel('related_page', 'demo.PublisherPage'),
  97. ]
  98. ``PageChooserPanel`` takes one required argument, the field name. Optionally, specifying a page type (in the form of an ``"appname.modelname"`` string) will filter the chooser to display only pages of that type. A list or tuple of page types can also be passed in, to allow choosing a page that matches any of those page types:
  99. .. code-block:: python
  100. PageChooserPanel('related_page', ['demo.PublisherPage', 'demo.AuthorPage'])
  101. Passing ``can_choose_root=True`` will allow the editor to choose the tree root as a page. Normally this would be undesirable, since the tree root is never a usable page, but in some specialised cases it may be appropriate; for example, a page with an automatic "related articles" feed could use a PageChooserPanel to select which subsection articles will be taken from, with the root corresponding to 'everywhere'.
  102. ImageChooserPanel
  103. ~~~~~~~~~~~~~~~~~
  104. .. module:: wagtail.images.edit_handlers
  105. .. class:: ImageChooserPanel(field_name)
  106. Wagtail includes a unified image library, which you can access in your models through the :class:`~wagtail.images.models.Image` model and the ``ImageChooserPanel`` chooser. Here's how:
  107. .. code-block:: python
  108. from wagtail.images.models import Image
  109. from wagtail.images.edit_handlers import ImageChooserPanel
  110. class BookPage(Page):
  111. cover = models.ForeignKey(
  112. 'wagtailimages.Image',
  113. null=True,
  114. blank=True,
  115. on_delete=models.SET_NULL,
  116. related_name='+'
  117. )
  118. content_panels = Page.content_panels + [
  119. ImageChooserPanel('cover'),
  120. ]
  121. Django's default behaviour is to "cascade" deletions through a ForeignKey relationship, which may not be what you want. This is why the :attr:`~django.db.models.Field.null`, :attr:`~django.db.models.Field.blank`, and :attr:`~django.db.models.ForeignKey.on_delete` parameters should be set to allow for an empty field. ``ImageChooserPanel`` takes only one argument: the name of the field.
  122. Displaying ``Image`` objects in a template requires the use of a template tag. See :ref:`image_tag`.
  123. FormSubmissionsPanel
  124. ~~~~~~~~~~~~~~~~~~~~
  125. .. module:: wagtail.contrib.forms.edit_handlers
  126. .. class:: FormSubmissionsPanel
  127. This panel adds a single, read-only section in the edit interface for pages implementing the :class:`~wagtail.contrib.forms.models.AbstractForm` model.
  128. It includes the number of total submissions for the given form and also a link to the listing of submissions.
  129. .. code-block:: python
  130. from wagtail.contrib.forms.models import AbstractForm
  131. from wagtail.contrib.forms.edit_handlers import FormSubmissionsPanel
  132. class ContactFormPage(AbstractForm):
  133. content_panels = [
  134. FormSubmissionsPanel(),
  135. ]
  136. DocumentChooserPanel
  137. ~~~~~~~~~~~~~~~~~~~~
  138. .. module:: wagtail.documents.edit_handlers
  139. .. class:: DocumentChooserPanel(field_name)
  140. For files in other formats, Wagtail provides a generic file store through the :class:`~wagtail.documents.models.Document` model:
  141. .. code-block:: python
  142. from wagtail.documents.models import Document
  143. from wagtail.documents.edit_handlers import DocumentChooserPanel
  144. class BookPage(Page):
  145. book_file = models.ForeignKey(
  146. 'wagtaildocs.Document',
  147. null=True,
  148. blank=True,
  149. on_delete=models.SET_NULL,
  150. related_name='+'
  151. )
  152. content_panels = Page.content_panels + [
  153. DocumentChooserPanel('book_file'),
  154. ]
  155. As with images, Wagtail documents should also have the appropriate extra parameters to prevent cascade deletions across a ForeignKey relationship. ``DocumentChooserPanel`` takes only one argument: the name of the field.
  156. SnippetChooserPanel
  157. ~~~~~~~~~~~~~~~~~~~
  158. .. module:: wagtail.snippets.edit_handlers
  159. .. class:: SnippetChooserPanel(field_name, snippet_type=None)
  160. Snippets are vanilla Django models you create yourself without a Wagtail-provided base class. A chooser, ``SnippetChooserPanel``, is provided which takes the field name as an argument.
  161. .. code-block:: python
  162. from wagtail.snippets.edit_handlers import SnippetChooserPanel
  163. class BookPage(Page):
  164. advert = models.ForeignKey(
  165. 'demo.Advert',
  166. null=True,
  167. blank=True,
  168. on_delete=models.SET_NULL,
  169. related_name='+'
  170. )
  171. content_panels = Page.content_panels + [
  172. SnippetChooserPanel('advert'),
  173. ]
  174. See :ref:`snippets` for more information.
  175. Field Customisation
  176. -------------------
  177. By adding CSS classes to your panel definitions or adding extra parameters to your field definitions, you can control much of how your fields will display in the Wagtail page editing interface. Wagtail's page editing interface takes much of its behaviour from Django's admin, so you may find many options for customisation covered there. (See :doc:`Django model field reference <ref/models/fields>`).
  178. Full-Width Input
  179. ~~~~~~~~~~~~~~~~
  180. Use ``classname="full"`` to make a field (input element) stretch the full width of the Wagtail page editor. This will not work if the field is encapsulated in a :class:`~wagtail.admin.edit_handlers.MultiFieldPanel`, which places its child fields into a formset.
  181. Titles
  182. ~~~~~~
  183. Use ``classname="title"`` to make Page's built-in title field stand out with more vertical padding.
  184. Placeholder Text
  185. ~~~~~~~~~~~~~~~~
  186. By default, Wagtail uses the field's label as placeholder text. To change it, pass to the FieldPanel a widget with a placeholder attribute set to your desired text. You can select widgets from :doc:`Django's form widgets <django:ref/forms/widgets>`, or any of the Wagtail's widgets found in ``wagtail.admin.widgets``.
  187. For example, to customize placeholders for a Book model exposed via ModelAdmin:
  188. .. code-block:: python
  189. # models.py
  190. from django import forms # the default Django widgets live here
  191. from wagtail.admin import widgets # to use Wagtail's special datetime widget
  192. class Book(models.Model):
  193. title = models.CharField(max_length=256)
  194. release_date = models.DateField()
  195. price = models.DecimalField(max_digits=5, decimal_places=2)
  196. # you can create them separately
  197. title_widget = forms.TextInput(
  198. attrs = {
  199. 'placeholder': 'Enter Full Title'
  200. }
  201. )
  202. # using the correct widget for your field type and desired effect
  203. date_widget = widgets.AdminDateInput(
  204. attrs = {
  205. 'placeholder': 'dd-mm-yyyy'
  206. }
  207. )
  208. panels = [
  209. FieldPanel('title', widget=title_widget), # then add them as a variable
  210. FieldPanel('release_date', widget=date_widget),
  211. FieldPanel('price', widget=forms.NumberInput(attrs={'placeholder': 'Retail price on release'})) # or directly inline
  212. ]
  213. Required Fields
  214. ~~~~~~~~~~~~~~~
  215. To make input or chooser selection mandatory for a field, add :attr:`blank=False <django.db.models.Field.blank>` to its model definition.
  216. Hiding Fields
  217. ~~~~~~~~~~~~~
  218. Without a panel definition, a default form field (without label) will be used to represent your fields. If you intend to hide a field on the Wagtail page editor, define the field with :attr:`editable=False <django.db.models.Field.editable>`.
  219. .. _inline_panels:
  220. Inline Panels and Model Clusters
  221. --------------------------------
  222. The ``django-modelcluster`` module allows for streamlined relation of extra models to a Wagtail page via a ForeignKey-like relationship called ``ParentalKey``. Normally, your related objects "cluster" would need to be created beforehand (or asynchronously) before being linked to a Page; however, objects related to a Wagtail page via ``ParentalKey`` can be created on-the-fly and saved to a draft revision of a ``Page`` object.
  223. Let's look at the example of adding related links to a :class:`~wagtail.core.models.Page`-derived model. We want to be able to add as many as we like, assign an order, and do all of this without leaving the page editing screen.
  224. .. code-block:: python
  225. from wagtail.core.models import Orderable, Page
  226. from modelcluster.fields import ParentalKey
  227. # The abstract model for related links, complete with panels
  228. class RelatedLink(models.Model):
  229. title = models.CharField(max_length=255)
  230. link_external = models.URLField("External link", blank=True)
  231. panels = [
  232. FieldPanel('title'),
  233. FieldPanel('link_external'),
  234. ]
  235. class Meta:
  236. abstract = True
  237. # The real model which combines the abstract model, an
  238. # Orderable helper class, and what amounts to a ForeignKey link
  239. # to the model we want to add related links to (BookPage)
  240. class BookPageRelatedLinks(Orderable, RelatedLink):
  241. page = ParentalKey('demo.BookPage', on_delete=models.CASCADE, related_name='related_links')
  242. class BookPage(Page):
  243. # ...
  244. content_panels = Page.content_panels + [
  245. InlinePanel('related_links', label="Related Links"),
  246. ]
  247. The ``RelatedLink`` class is a vanilla Django abstract model. The ``BookPageRelatedLinks`` model extends it with capability for being ordered in the Wagtail interface via the ``Orderable`` class as well as adding a ``page`` property which links the model to the ``BookPage`` model we're adding the related links objects to. Finally, in the panel definitions for ``BookPage``, we'll add an :class:`~wagtail.admin.edit_handlers.InlinePanel` to provide an interface for it all. Let's look again at the parameters that :class:`~wagtail.admin.edit_handlers.InlinePanel` accepts:
  248. .. code-block:: python
  249. InlinePanel( relation_name, panels=None, heading='', label='', help_text='', min_num=None, max_num=None )
  250. The ``relation_name`` is the ``related_name`` label given to the cluster's ``ParentalKey`` relation. You can add the ``panels`` manually or make them part of the cluster model. ``heading`` and ``help_text`` provide a heading and caption, respectively, for the Wagtail editor. ``label`` sets the text on the add button, and is used as the heading when ``heading`` is not present. Finally, ``min_num`` and ``max_num`` allow you to set the minimum/maximum number of forms that the user must submit.
  251. For another example of using model clusters, see :ref:`tagging`
  252. For more on ``django-modelcluster``, visit `the django-modelcluster github project page`_.
  253. .. _the django-modelcluster github project page: https://github.com/torchbox/django-modelcluster