wagtail/docs/advanced_topics/customisation/page_editing_interface.rst

297 wiersze
14 KiB
ReStructuredText
Czysty Zwykły widok Historia

Customising the editing interface
=================================
.. _customising_the_tabbed_interface:
Customising the tabbed interface
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As standard, Wagtail organises panels for pages into three tabs: 'Content', 'Promote' and 'Settings'. For snippets Wagtail puts all panels into one page. Depending on the requirements of your site, you may wish to customise this for specific page types or snippets - for example, adding an additional tab for sidebar content. This can be done by specifying an ``edit_handler`` attribute on the page or snippet model. For example:
.. code-block:: python
Rename wagtail.wagtailadmin to wagtail.admin Conflicts: docs/advanced_topics/customisation/admin_templates.rst docs/advanced_topics/customisation/page_editing_interface.rst docs/advanced_topics/i18n/duplicate_tree.rst docs/advanced_topics/jinja2.rst docs/advanced_topics/settings.rst docs/getting_started/integrating_into_django.rst docs/getting_started/tutorial.rst docs/reference/hooks.rst docs/reference/pages/panels.rst docs/topics/pages.rst docs/topics/streamfield.rst wagtail/admin/blocks.py wagtail/admin/checks.py wagtail/admin/edit_handlers.py wagtail/admin/forms.py wagtail/admin/rich_text.py wagtail/admin/search.py wagtail/admin/site_summary.py wagtail/admin/templatetags/wagtailadmin_tags.py wagtail/admin/tests/test_admin_search.py wagtail/admin/tests/test_buttons_hooks.py wagtail/admin/tests/test_compare.py wagtail/admin/tests/test_edit_handlers.py wagtail/admin/tests/test_page_chooser.py wagtail/admin/tests/test_pages_views.py wagtail/admin/tests/test_rich_text.py wagtail/admin/tests/test_widgets.py wagtail/admin/tests/tests.py wagtail/admin/urls/__init__.py wagtail/admin/views/account.py wagtail/admin/views/chooser.py wagtail/admin/views/collection_privacy.py wagtail/admin/views/collections.py wagtail/admin/views/home.py wagtail/admin/views/page_privacy.py wagtail/admin/viewsets/model.py wagtail/admin/wagtail_hooks.py wagtail/admin/widgets.py wagtail/contrib/settings/registry.py wagtail/contrib/wagtailsearchpromotions/wagtail_hooks.py wagtail/contrib/wagtailstyleguide/wagtail_hooks.py wagtail/project_template/project_name/settings/base.py wagtail/project_template/project_name/urls.py wagtail/tests/non_root_urls.py wagtail/tests/settings.py wagtail/tests/snippets/models.py wagtail/tests/testapp/models.py wagtail/tests/testapp/wagtail_hooks.py wagtail/tests/urls.py wagtail/wagtaildocs/models.py wagtail/wagtaildocs/views/chooser.py wagtail/wagtaildocs/wagtail_hooks.py wagtail/wagtailembeds/wagtail_hooks.py wagtail/wagtailforms/models.py wagtail/wagtailforms/tests/test_views.py wagtail/wagtailforms/views.py wagtail/wagtailforms/wagtail_hooks.py wagtail/wagtailimages/models.py wagtail/wagtailimages/views/chooser.py wagtail/wagtailimages/wagtail_hooks.py wagtail/wagtailredirects/forms.py wagtail/wagtailredirects/wagtail_hooks.py wagtail/wagtailsites/forms.py wagtail/wagtailsites/views.py wagtail/wagtailsites/wagtail_hooks.py wagtail/wagtailsnippets/tests.py wagtail/wagtailsnippets/wagtail_hooks.py wagtail/wagtailusers/forms.py wagtail/wagtailusers/views/groups.py wagtail/wagtailusers/wagtail_hooks.py
2017-11-17 10:44:34 +00:00
from wagtail.admin.edit_handlers import TabbedInterface, ObjectList
class BlogPage(Page):
# field definitions omitted
content_panels = [
FieldPanel('title', classname="full title"),
FieldPanel('date'),
FieldPanel('body', classname="full"),
]
sidebar_content_panels = [
SnippetChooserPanel('advert'),
InlinePanel('related_links', label="Related links"),
]
edit_handler = TabbedInterface([
ObjectList(content_panels, heading='Content'),
ObjectList(sidebar_content_panels, heading='Sidebar content'),
ObjectList(Page.promote_panels, heading='Promote'),
ObjectList(Page.settings_panels, heading='Settings', classname="settings"),
])
.. _rich-text:
Rich Text (HTML)
~~~~~~~~~~~~~~~~
Wagtail provides a general-purpose WYSIWYG editor for creating rich text content (HTML) and embedding media such as images, video, and documents. To include this in your models, use the :class:`~wagtail.core.fields.RichTextField` function when defining a model field:
.. code-block:: python
from wagtail.core.fields import RichTextField
Rename wagtail.wagtailadmin to wagtail.admin Conflicts: docs/advanced_topics/customisation/admin_templates.rst docs/advanced_topics/customisation/page_editing_interface.rst docs/advanced_topics/i18n/duplicate_tree.rst docs/advanced_topics/jinja2.rst docs/advanced_topics/settings.rst docs/getting_started/integrating_into_django.rst docs/getting_started/tutorial.rst docs/reference/hooks.rst docs/reference/pages/panels.rst docs/topics/pages.rst docs/topics/streamfield.rst wagtail/admin/blocks.py wagtail/admin/checks.py wagtail/admin/edit_handlers.py wagtail/admin/forms.py wagtail/admin/rich_text.py wagtail/admin/search.py wagtail/admin/site_summary.py wagtail/admin/templatetags/wagtailadmin_tags.py wagtail/admin/tests/test_admin_search.py wagtail/admin/tests/test_buttons_hooks.py wagtail/admin/tests/test_compare.py wagtail/admin/tests/test_edit_handlers.py wagtail/admin/tests/test_page_chooser.py wagtail/admin/tests/test_pages_views.py wagtail/admin/tests/test_rich_text.py wagtail/admin/tests/test_widgets.py wagtail/admin/tests/tests.py wagtail/admin/urls/__init__.py wagtail/admin/views/account.py wagtail/admin/views/chooser.py wagtail/admin/views/collection_privacy.py wagtail/admin/views/collections.py wagtail/admin/views/home.py wagtail/admin/views/page_privacy.py wagtail/admin/viewsets/model.py wagtail/admin/wagtail_hooks.py wagtail/admin/widgets.py wagtail/contrib/settings/registry.py wagtail/contrib/wagtailsearchpromotions/wagtail_hooks.py wagtail/contrib/wagtailstyleguide/wagtail_hooks.py wagtail/project_template/project_name/settings/base.py wagtail/project_template/project_name/urls.py wagtail/tests/non_root_urls.py wagtail/tests/settings.py wagtail/tests/snippets/models.py wagtail/tests/testapp/models.py wagtail/tests/testapp/wagtail_hooks.py wagtail/tests/urls.py wagtail/wagtaildocs/models.py wagtail/wagtaildocs/views/chooser.py wagtail/wagtaildocs/wagtail_hooks.py wagtail/wagtailembeds/wagtail_hooks.py wagtail/wagtailforms/models.py wagtail/wagtailforms/tests/test_views.py wagtail/wagtailforms/views.py wagtail/wagtailforms/wagtail_hooks.py wagtail/wagtailimages/models.py wagtail/wagtailimages/views/chooser.py wagtail/wagtailimages/wagtail_hooks.py wagtail/wagtailredirects/forms.py wagtail/wagtailredirects/wagtail_hooks.py wagtail/wagtailsites/forms.py wagtail/wagtailsites/views.py wagtail/wagtailsites/wagtail_hooks.py wagtail/wagtailsnippets/tests.py wagtail/wagtailsnippets/wagtail_hooks.py wagtail/wagtailusers/forms.py wagtail/wagtailusers/views/groups.py wagtail/wagtailusers/wagtail_hooks.py
2017-11-17 10:44:34 +00:00
from wagtail.admin.edit_handlers import FieldPanel
class BookPage(Page):
book_text = RichTextField()
content_panels = Page.content_panels + [
FieldPanel('body', classname="full"),
]
:class:`~wagtail.core.fields.RichTextField` inherits from Django's basic ``TextField`` field, so you can pass any field parameters into :class:`~wagtail.core.fields.RichTextField` as if using a normal Django field. This field does not need a special panel and can be defined with ``FieldPanel``.
However, template output from :class:`~wagtail.core.fields.RichTextField` is special and need to be filtered to preserve embedded content. See :ref:`rich-text-filter`.
2017-07-26 14:57:38 +00:00
.. _rich_text_features:
Limiting features in a rich text field
--------------------------------------
By default, the rich text editor provides users with a wide variety of options for text formatting and inserting embedded content such as images. However, we may wish to restrict a rich text field to a more limited set of features - for example:
* The field might be intended for a short text snippet, such as a summary to be pulled out on index pages, where embedded images or videos would be inappropriate;
* When page content is defined using :ref:`StreamField <streamfield>`, elements such as headings, images and videos are usually given their own block types, alongside a rich text block type used for ordinary paragraph text; in this case, allowing headings and images to also exist within the rich text content is redundant (and liable to result in inconsistent designs).
This can be achieved by passing a ``features`` keyword argument to ``RichTextField``, with a list of identifiers for the features you wish to allow:
.. code-block:: python
body = RichTextField(features=['h2', 'h3', 'bold', 'italic', 'link'])
2017-07-26 14:57:38 +00:00
The feature identifiers provided on a default Wagtail installation are as follows:
2017-07-26 14:57:38 +00:00
* ``h1``, ``h2``, ``h3``, ``h4``, ``h5``, ``h6`` - heading elements
* ``bold``, ``italic`` - bold / italic text
* ``ol``, ``ul`` - ordered / unordered lists
* ``hr`` - horizontal rules
* ``link`` - page, external and email links
* ``document-link`` - links to documents
* ``image`` - embedded images
* ``embed`` - embedded media (see :ref:`embedded_content`)
Adding new features to this list is generally a two step process:
* Create a plugin that extends the editor with a new toolbar button or other control(s) to manage the rich text formatting of the feature.
* Create conversion rules to define how content from the editor should be filtered or transformed before storage, and front-end HTML output.
Both of these steps are performed through the ``register_rich_text_features`` hook (see :ref:`admin_hooks`). The hook function is triggered on startup, and receives a *feature registry* object as its argument; this object keeps track of the behaviours associated with each feature identifier.
This process for adding new features is described in the following sections.
.. _extending_wysiwyg:
Extending the WYSIWYG Editor (``hallo.js``)
+++++++++++++++++++++++++++++++++++++++++++
.. note::
The customisations described here are only available on the hallo.js rich text editor used on Wagtail 1.x. To use hallo.js on Wagtail 2.x, add the following to your settings:
.. code-block:: python
WAGTAILADMIN_RICH_TEXT_EDITORS = {
'default': {
'WIDGET': 'wagtail.admin.rich_text.HalloRichTextArea'
}
}
2017-07-26 14:57:38 +00:00
Wagtail's rich text editor is built on ``hallo.js``, and its functionality can be extended through plugins. For information on developing custom ``hallo.js`` plugins, see the project's page: https://github.com/bergie/hallo
Once the plugin has been created, it should be registered through the feature registry's ``register_editor_plugin(editor, feature_name, plugin)`` method. For a ``hallo.js`` plugin, the ``editor`` parameter should always be ``'hallo'``.
A plugin ``halloblockquote``, implemented in ``myapp/js/hallo-blockquote.js``, that adds support for the ``<blockquote>`` tag, would be registered under the feature name ``block-quote`` as follows:
2017-07-26 14:57:38 +00:00
.. code-block:: python
Rename wagtail.wagtailadmin to wagtail.admin Conflicts: docs/advanced_topics/customisation/admin_templates.rst docs/advanced_topics/customisation/page_editing_interface.rst docs/advanced_topics/i18n/duplicate_tree.rst docs/advanced_topics/jinja2.rst docs/advanced_topics/settings.rst docs/getting_started/integrating_into_django.rst docs/getting_started/tutorial.rst docs/reference/hooks.rst docs/reference/pages/panels.rst docs/topics/pages.rst docs/topics/streamfield.rst wagtail/admin/blocks.py wagtail/admin/checks.py wagtail/admin/edit_handlers.py wagtail/admin/forms.py wagtail/admin/rich_text.py wagtail/admin/search.py wagtail/admin/site_summary.py wagtail/admin/templatetags/wagtailadmin_tags.py wagtail/admin/tests/test_admin_search.py wagtail/admin/tests/test_buttons_hooks.py wagtail/admin/tests/test_compare.py wagtail/admin/tests/test_edit_handlers.py wagtail/admin/tests/test_page_chooser.py wagtail/admin/tests/test_pages_views.py wagtail/admin/tests/test_rich_text.py wagtail/admin/tests/test_widgets.py wagtail/admin/tests/tests.py wagtail/admin/urls/__init__.py wagtail/admin/views/account.py wagtail/admin/views/chooser.py wagtail/admin/views/collection_privacy.py wagtail/admin/views/collections.py wagtail/admin/views/home.py wagtail/admin/views/page_privacy.py wagtail/admin/viewsets/model.py wagtail/admin/wagtail_hooks.py wagtail/admin/widgets.py wagtail/contrib/settings/registry.py wagtail/contrib/wagtailsearchpromotions/wagtail_hooks.py wagtail/contrib/wagtailstyleguide/wagtail_hooks.py wagtail/project_template/project_name/settings/base.py wagtail/project_template/project_name/urls.py wagtail/tests/non_root_urls.py wagtail/tests/settings.py wagtail/tests/snippets/models.py wagtail/tests/testapp/models.py wagtail/tests/testapp/wagtail_hooks.py wagtail/tests/urls.py wagtail/wagtaildocs/models.py wagtail/wagtaildocs/views/chooser.py wagtail/wagtaildocs/wagtail_hooks.py wagtail/wagtailembeds/wagtail_hooks.py wagtail/wagtailforms/models.py wagtail/wagtailforms/tests/test_views.py wagtail/wagtailforms/views.py wagtail/wagtailforms/wagtail_hooks.py wagtail/wagtailimages/models.py wagtail/wagtailimages/views/chooser.py wagtail/wagtailimages/wagtail_hooks.py wagtail/wagtailredirects/forms.py wagtail/wagtailredirects/wagtail_hooks.py wagtail/wagtailsites/forms.py wagtail/wagtailsites/views.py wagtail/wagtailsites/wagtail_hooks.py wagtail/wagtailsnippets/tests.py wagtail/wagtailsnippets/wagtail_hooks.py wagtail/wagtailusers/forms.py wagtail/wagtailusers/views/groups.py wagtail/wagtailusers/wagtail_hooks.py
2017-11-17 10:44:34 +00:00
from wagtail.admin.rich_text import HalloPlugin
from wagtail.core import hooks
2017-07-26 14:57:38 +00:00
@hooks.register('register_rich_text_features')
def register_embed_feature(features):
features.register_editor_plugin(
'hallo', 'block-quote',
2017-07-26 14:57:38 +00:00
HalloPlugin(
name='halloblockquote',
js=['myapp/js/hallo-blockquote.js'],
2017-07-26 14:57:38 +00:00
)
)
The constructor for ``HalloPlugin`` accepts the following keyword arguments:
* ``name`` - the plugin name as defined in the Javascript code. ``hallo.js`` plugin names are prefixed with the ``"IKS."`` namespace, but the name passed here should be without the prefix.
* ``options`` - a dictionary (or other JSON-serialisable object) of options to be passed to the Javascript plugin code on initialisation
* ``js`` - a list of Javascript files to be imported for this plugin, defined in the same way as a `Django form media <https://docs.djangoproject.com/en/1.11/topics/forms/media/>`_ definition
* ``css`` - a dictionary of CSS files to be imported for this plugin, defined in the same way as a `Django form media <https://docs.djangoproject.com/en/1.11/topics/forms/media/>`_ definition
* ``order`` - an index number (default 100) specifying the order in which plugins should be listed, which in turn determines the order buttons will appear in the toolbar
2017-10-09 22:41:40 +00:00
To have a feature active by default (i.e. on ``RichTextFields`` that do not define an explicit ``features`` list), add it to the ``default_features`` list on the ``features`` object:
2017-07-26 14:57:38 +00:00
.. code-block:: python
from django.utils.html import format_html
@hooks.register('register_rich_text_features')
def register_blockquote_feature(features):
features.register_editor_plugin(
'hallo', 'block-quote',
2017-07-26 14:57:38 +00:00
# ...
)
features.default_features.append('block-quote')
.. _whitelisting_rich_text_elements:
Whitelisting rich text elements
+++++++++++++++++++++++++++++++
After extending the editor to support a new HTML element, you'll need to add it to the whitelist of permitted elements - Wagtail's standard behaviour is to strip out unrecognised elements, to prevent editors from inserting styles and scripts (either deliberately, or inadvertently through copy-and-paste) that the developer didn't account for.
Elements can be added to the whitelist through the feature registry's ``register_converter_rule(converter, feature_name, ruleset)`` method. When the ``hallo.js`` editor is in use, the ``converter`` parameter should always be ``'editorhtml'``.
The following code will add the ``<blockquote>`` element to the whitelist whenever the ``block-quote`` feature is active:
.. code-block:: python
from wagtail.admin.rich_text.converters.editor_html import WhitelistRule
from wagtail.core.whitelist import allow_without_attributes
@hooks.register('register_rich_text_features')
def register_blockquote_feature(features):
features.register_converter_rule('editorhtml', 'block-quote', [
WhitelistRule('blockquote', allow_without_attributes),
])
``WhitelistRule`` is passed the element name, and a callable which will perform some kind of manipulation of the element whenever it is encountered. This callable receives the element as a `BeautifulSoup <http://www.crummy.com/software/BeautifulSoup/bs4/doc/>`_ Tag object.
The ``wagtail.core.whitelist`` module provides a few helper functions to assist in defining these handlers: ``allow_without_attributes``, a handler which preserves the element but strips out all of its attributes, and ``attribute_rule`` which accepts a dict specifying how to handle each attribute, and returns a handler function. This dict will map attribute names to either True (indicating that the attribute should be kept), False (indicating that it should be dropped), or a callable (which takes the initial attribute value and returns either a final value for the attribute, or None to drop the attribute).
.. _rich_text_image_formats:
Image Formats in the Rich Text Editor
-------------------------------------
On loading, Wagtail will search for any app with the file ``image_formats.py`` and execute the contents. This provides a way to customise the formatting options shown to the editor when inserting images in the :class:`~wagtail.core.fields.RichTextField` editor.
As an example, add a "thumbnail" format:
.. code-block:: python
# image_formats.py
from wagtail.images.formats import Format, register_image_format
register_image_format(Format('thumbnail', 'Thumbnail', 'richtext-image thumbnail', 'max-120x120'))
To begin, import the ``Format`` class, ``register_image_format`` function, and optionally ``unregister_image_format`` function. To register a new ``Format``, call the ``register_image_format`` with the ``Format`` object as the argument. The ``Format`` class takes the following constructor arguments:
``name``
The unique key used to identify the format. To unregister this format, call ``unregister_image_format`` with this string as the only argument.
``label``
The label used in the chooser form when inserting the image into the :class:`~wagtail.core.fields.RichTextField`.
``classnames``
The string to assign to the ``class`` attribute of the generated ``<img>`` tag.
.. note::
Any class names you provide must have CSS rules matching them written separately, as part of the front end CSS code. Specifying a ``classnames`` value of ``left`` will only ensure that class is output in the generated markup, it won't cause the image to align itself left.
``filter_spec``
The string specification to create the image rendition. For more, see the :ref:`image_tag`.
To unregister, call ``unregister_image_format`` with the string of the ``name`` of the ``Format`` as the only argument.
.. _custom_edit_handler_forms:
Customising generated forms
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Rename wagtail.wagtailadmin to wagtail.admin Conflicts: docs/advanced_topics/customisation/admin_templates.rst docs/advanced_topics/customisation/page_editing_interface.rst docs/advanced_topics/i18n/duplicate_tree.rst docs/advanced_topics/jinja2.rst docs/advanced_topics/settings.rst docs/getting_started/integrating_into_django.rst docs/getting_started/tutorial.rst docs/reference/hooks.rst docs/reference/pages/panels.rst docs/topics/pages.rst docs/topics/streamfield.rst wagtail/admin/blocks.py wagtail/admin/checks.py wagtail/admin/edit_handlers.py wagtail/admin/forms.py wagtail/admin/rich_text.py wagtail/admin/search.py wagtail/admin/site_summary.py wagtail/admin/templatetags/wagtailadmin_tags.py wagtail/admin/tests/test_admin_search.py wagtail/admin/tests/test_buttons_hooks.py wagtail/admin/tests/test_compare.py wagtail/admin/tests/test_edit_handlers.py wagtail/admin/tests/test_page_chooser.py wagtail/admin/tests/test_pages_views.py wagtail/admin/tests/test_rich_text.py wagtail/admin/tests/test_widgets.py wagtail/admin/tests/tests.py wagtail/admin/urls/__init__.py wagtail/admin/views/account.py wagtail/admin/views/chooser.py wagtail/admin/views/collection_privacy.py wagtail/admin/views/collections.py wagtail/admin/views/home.py wagtail/admin/views/page_privacy.py wagtail/admin/viewsets/model.py wagtail/admin/wagtail_hooks.py wagtail/admin/widgets.py wagtail/contrib/settings/registry.py wagtail/contrib/wagtailsearchpromotions/wagtail_hooks.py wagtail/contrib/wagtailstyleguide/wagtail_hooks.py wagtail/project_template/project_name/settings/base.py wagtail/project_template/project_name/urls.py wagtail/tests/non_root_urls.py wagtail/tests/settings.py wagtail/tests/snippets/models.py wagtail/tests/testapp/models.py wagtail/tests/testapp/wagtail_hooks.py wagtail/tests/urls.py wagtail/wagtaildocs/models.py wagtail/wagtaildocs/views/chooser.py wagtail/wagtaildocs/wagtail_hooks.py wagtail/wagtailembeds/wagtail_hooks.py wagtail/wagtailforms/models.py wagtail/wagtailforms/tests/test_views.py wagtail/wagtailforms/views.py wagtail/wagtailforms/wagtail_hooks.py wagtail/wagtailimages/models.py wagtail/wagtailimages/views/chooser.py wagtail/wagtailimages/wagtail_hooks.py wagtail/wagtailredirects/forms.py wagtail/wagtailredirects/wagtail_hooks.py wagtail/wagtailsites/forms.py wagtail/wagtailsites/views.py wagtail/wagtailsites/wagtail_hooks.py wagtail/wagtailsnippets/tests.py wagtail/wagtailsnippets/wagtail_hooks.py wagtail/wagtailusers/forms.py wagtail/wagtailusers/views/groups.py wagtail/wagtailusers/wagtail_hooks.py
2017-11-17 10:44:34 +00:00
.. class:: wagtail.admin.forms.WagtailAdminModelForm
.. class:: wagtail.admin.forms.WagtailAdminPageForm
Wagtail automatically generates forms using the panels configured on the model.
Rename wagtail.wagtailadmin to wagtail.admin Conflicts: docs/advanced_topics/customisation/admin_templates.rst docs/advanced_topics/customisation/page_editing_interface.rst docs/advanced_topics/i18n/duplicate_tree.rst docs/advanced_topics/jinja2.rst docs/advanced_topics/settings.rst docs/getting_started/integrating_into_django.rst docs/getting_started/tutorial.rst docs/reference/hooks.rst docs/reference/pages/panels.rst docs/topics/pages.rst docs/topics/streamfield.rst wagtail/admin/blocks.py wagtail/admin/checks.py wagtail/admin/edit_handlers.py wagtail/admin/forms.py wagtail/admin/rich_text.py wagtail/admin/search.py wagtail/admin/site_summary.py wagtail/admin/templatetags/wagtailadmin_tags.py wagtail/admin/tests/test_admin_search.py wagtail/admin/tests/test_buttons_hooks.py wagtail/admin/tests/test_compare.py wagtail/admin/tests/test_edit_handlers.py wagtail/admin/tests/test_page_chooser.py wagtail/admin/tests/test_pages_views.py wagtail/admin/tests/test_rich_text.py wagtail/admin/tests/test_widgets.py wagtail/admin/tests/tests.py wagtail/admin/urls/__init__.py wagtail/admin/views/account.py wagtail/admin/views/chooser.py wagtail/admin/views/collection_privacy.py wagtail/admin/views/collections.py wagtail/admin/views/home.py wagtail/admin/views/page_privacy.py wagtail/admin/viewsets/model.py wagtail/admin/wagtail_hooks.py wagtail/admin/widgets.py wagtail/contrib/settings/registry.py wagtail/contrib/wagtailsearchpromotions/wagtail_hooks.py wagtail/contrib/wagtailstyleguide/wagtail_hooks.py wagtail/project_template/project_name/settings/base.py wagtail/project_template/project_name/urls.py wagtail/tests/non_root_urls.py wagtail/tests/settings.py wagtail/tests/snippets/models.py wagtail/tests/testapp/models.py wagtail/tests/testapp/wagtail_hooks.py wagtail/tests/urls.py wagtail/wagtaildocs/models.py wagtail/wagtaildocs/views/chooser.py wagtail/wagtaildocs/wagtail_hooks.py wagtail/wagtailembeds/wagtail_hooks.py wagtail/wagtailforms/models.py wagtail/wagtailforms/tests/test_views.py wagtail/wagtailforms/views.py wagtail/wagtailforms/wagtail_hooks.py wagtail/wagtailimages/models.py wagtail/wagtailimages/views/chooser.py wagtail/wagtailimages/wagtail_hooks.py wagtail/wagtailredirects/forms.py wagtail/wagtailredirects/wagtail_hooks.py wagtail/wagtailsites/forms.py wagtail/wagtailsites/views.py wagtail/wagtailsites/wagtail_hooks.py wagtail/wagtailsnippets/tests.py wagtail/wagtailsnippets/wagtail_hooks.py wagtail/wagtailusers/forms.py wagtail/wagtailusers/views/groups.py wagtail/wagtailusers/wagtail_hooks.py
2017-11-17 10:44:34 +00:00
By default, this form subclasses :class:`~wagtail.admin.forms.WagtailAdminModelForm`,
or :class:`~wagtail.admin.forms.WagtailAdminPageForm` for pages.
A custom base form class can be configured by setting the :attr:`base_form_class` attribute on any model.
Rename wagtail.wagtailadmin to wagtail.admin Conflicts: docs/advanced_topics/customisation/admin_templates.rst docs/advanced_topics/customisation/page_editing_interface.rst docs/advanced_topics/i18n/duplicate_tree.rst docs/advanced_topics/jinja2.rst docs/advanced_topics/settings.rst docs/getting_started/integrating_into_django.rst docs/getting_started/tutorial.rst docs/reference/hooks.rst docs/reference/pages/panels.rst docs/topics/pages.rst docs/topics/streamfield.rst wagtail/admin/blocks.py wagtail/admin/checks.py wagtail/admin/edit_handlers.py wagtail/admin/forms.py wagtail/admin/rich_text.py wagtail/admin/search.py wagtail/admin/site_summary.py wagtail/admin/templatetags/wagtailadmin_tags.py wagtail/admin/tests/test_admin_search.py wagtail/admin/tests/test_buttons_hooks.py wagtail/admin/tests/test_compare.py wagtail/admin/tests/test_edit_handlers.py wagtail/admin/tests/test_page_chooser.py wagtail/admin/tests/test_pages_views.py wagtail/admin/tests/test_rich_text.py wagtail/admin/tests/test_widgets.py wagtail/admin/tests/tests.py wagtail/admin/urls/__init__.py wagtail/admin/views/account.py wagtail/admin/views/chooser.py wagtail/admin/views/collection_privacy.py wagtail/admin/views/collections.py wagtail/admin/views/home.py wagtail/admin/views/page_privacy.py wagtail/admin/viewsets/model.py wagtail/admin/wagtail_hooks.py wagtail/admin/widgets.py wagtail/contrib/settings/registry.py wagtail/contrib/wagtailsearchpromotions/wagtail_hooks.py wagtail/contrib/wagtailstyleguide/wagtail_hooks.py wagtail/project_template/project_name/settings/base.py wagtail/project_template/project_name/urls.py wagtail/tests/non_root_urls.py wagtail/tests/settings.py wagtail/tests/snippets/models.py wagtail/tests/testapp/models.py wagtail/tests/testapp/wagtail_hooks.py wagtail/tests/urls.py wagtail/wagtaildocs/models.py wagtail/wagtaildocs/views/chooser.py wagtail/wagtaildocs/wagtail_hooks.py wagtail/wagtailembeds/wagtail_hooks.py wagtail/wagtailforms/models.py wagtail/wagtailforms/tests/test_views.py wagtail/wagtailforms/views.py wagtail/wagtailforms/wagtail_hooks.py wagtail/wagtailimages/models.py wagtail/wagtailimages/views/chooser.py wagtail/wagtailimages/wagtail_hooks.py wagtail/wagtailredirects/forms.py wagtail/wagtailredirects/wagtail_hooks.py wagtail/wagtailsites/forms.py wagtail/wagtailsites/views.py wagtail/wagtailsites/wagtail_hooks.py wagtail/wagtailsnippets/tests.py wagtail/wagtailsnippets/wagtail_hooks.py wagtail/wagtailusers/forms.py wagtail/wagtailusers/views/groups.py wagtail/wagtailusers/wagtail_hooks.py
2017-11-17 10:44:34 +00:00
Custom forms for snippets must subclass :class:`~wagtail.admin.forms.WagtailAdminModelForm`,
and custom forms for pages must subclass :class:`~wagtail.admin.forms.WagtailAdminPageForm`.
This can be used to add non-model fields to the form, to automatically generate field content,
or to add custom validation logic for your models:
.. code-block:: python
from django import forms
import geocoder # not in Wagtail, for example only - http://geocoder.readthedocs.io/
Rename wagtail.wagtailadmin to wagtail.admin Conflicts: docs/advanced_topics/customisation/admin_templates.rst docs/advanced_topics/customisation/page_editing_interface.rst docs/advanced_topics/i18n/duplicate_tree.rst docs/advanced_topics/jinja2.rst docs/advanced_topics/settings.rst docs/getting_started/integrating_into_django.rst docs/getting_started/tutorial.rst docs/reference/hooks.rst docs/reference/pages/panels.rst docs/topics/pages.rst docs/topics/streamfield.rst wagtail/admin/blocks.py wagtail/admin/checks.py wagtail/admin/edit_handlers.py wagtail/admin/forms.py wagtail/admin/rich_text.py wagtail/admin/search.py wagtail/admin/site_summary.py wagtail/admin/templatetags/wagtailadmin_tags.py wagtail/admin/tests/test_admin_search.py wagtail/admin/tests/test_buttons_hooks.py wagtail/admin/tests/test_compare.py wagtail/admin/tests/test_edit_handlers.py wagtail/admin/tests/test_page_chooser.py wagtail/admin/tests/test_pages_views.py wagtail/admin/tests/test_rich_text.py wagtail/admin/tests/test_widgets.py wagtail/admin/tests/tests.py wagtail/admin/urls/__init__.py wagtail/admin/views/account.py wagtail/admin/views/chooser.py wagtail/admin/views/collection_privacy.py wagtail/admin/views/collections.py wagtail/admin/views/home.py wagtail/admin/views/page_privacy.py wagtail/admin/viewsets/model.py wagtail/admin/wagtail_hooks.py wagtail/admin/widgets.py wagtail/contrib/settings/registry.py wagtail/contrib/wagtailsearchpromotions/wagtail_hooks.py wagtail/contrib/wagtailstyleguide/wagtail_hooks.py wagtail/project_template/project_name/settings/base.py wagtail/project_template/project_name/urls.py wagtail/tests/non_root_urls.py wagtail/tests/settings.py wagtail/tests/snippets/models.py wagtail/tests/testapp/models.py wagtail/tests/testapp/wagtail_hooks.py wagtail/tests/urls.py wagtail/wagtaildocs/models.py wagtail/wagtaildocs/views/chooser.py wagtail/wagtaildocs/wagtail_hooks.py wagtail/wagtailembeds/wagtail_hooks.py wagtail/wagtailforms/models.py wagtail/wagtailforms/tests/test_views.py wagtail/wagtailforms/views.py wagtail/wagtailforms/wagtail_hooks.py wagtail/wagtailimages/models.py wagtail/wagtailimages/views/chooser.py wagtail/wagtailimages/wagtail_hooks.py wagtail/wagtailredirects/forms.py wagtail/wagtailredirects/wagtail_hooks.py wagtail/wagtailsites/forms.py wagtail/wagtailsites/views.py wagtail/wagtailsites/wagtail_hooks.py wagtail/wagtailsnippets/tests.py wagtail/wagtailsnippets/wagtail_hooks.py wagtail/wagtailusers/forms.py wagtail/wagtailusers/views/groups.py wagtail/wagtailusers/wagtail_hooks.py
2017-11-17 10:44:34 +00:00
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.admin.forms import WagtailAdminPageForm
from wagtail.core.models import Page
class EventPageForm(WagtailAdminPageForm):
address = forms.CharField()
def clean(self):
cleaned_data = super().clean()
# Make sure that the event starts before it ends
start_date = cleaned_data['start_date']
end_date = cleaned_data['end_date']
if start_date and end_date and start_date > end_date:
self.add_error('end_date', 'The end date must be after the start date')
return cleaned_data
def save(self, commit=True):
page = super().save(commit=False)
# Update the duration field from the submitted dates
page.duration = (page.end_date - page.start_date).days
# Fetch the location by geocoding the address
page.location = geocoder.arcgis(self.cleaned_data['address'])
if commit:
page.save()
return page
class EventPage(Page):
start_date = models.DateField()
end_date = models.DateField()
duration = models.IntegerField()
location = models.CharField(max_length=255)
content_panels = [
FieldPanel('title'),
FieldPanel('start_date'),
FieldPanel('end_date'),
FieldPanel('address'),
]
base_form_class = EventPageForm
Wagtail will generate a new subclass of this form for the model,
adding any fields defined in ``panels`` or ``content_panels``.
Any fields already defined on the model will not be overridden by these automatically added fields,
so the form field for a model field can be overridden by adding it to the custom form.