From 06abc497126a39c46b7e5ec94ee7a60e98cfec70 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Thu, 21 Apr 2022 16:18:38 +0100 Subject: [PATCH] Add documentation for the panel rendering mechanism --- docs/extending/forms.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/extending/forms.md b/docs/extending/forms.md index a84743111a..e55ca34408 100644 --- a/docs/extending/forms.md +++ b/docs/extending/forms.md @@ -56,3 +56,21 @@ class WagtailVideosAppConfig(AppConfig): ``` Wagtail's edit views for pages, snippets and ModelAdmin use `WagtailAdminModelForm` as standard, so this change will take effect across the Wagtail admin; a foreign key to `Video` on a page model will automatically use the `VideoChooser` widget, with no need to specify this explicitly. + + +## Panels + +Panels (also known as edit handlers until Wagtail 3.0) are Wagtail's mechanism for specifying the content and layout of a model form without having to write a template. They are used for the editing interface for pages and snippets, as well as the [ModelAdmin](/reference/contrib/modeladmin/index) and [site settings](/reference/contrib/settings) contrib modules. + +See [](/reference/pages/panels) for the set of panel types provided by Wagtail. All panels inherit from the base class `wagtail.admin.panels.Panel`. A single panel object (usually `ObjectList` or `TabbedInterface`) exists at the top level and is the only one directly accessed by the view code; panels containing child panels inherit from the base class `wagtail.admin.panels.PanelGroup` and take care of recursively calling methods on their child panels where appropriate. + +A view performs the following steps to render a model form through the panels mechanism: + +* The top-level panel object for the model is retrieved. Usually this is done by looking up the model's `edit_handler` property and falling back on an `ObjectList` consisting of children given by the model's `panels` property. However, it may come from elsewhere - for example, the ModelAdmin module allows defining it on the ModelAdmin configuration object. +* The view calls `bind_to_model` on the top-level panel, passing the model class, and this returns a clone of the panel with a `model` property. As part of this process the `on_model_bound` method is invoked on each child panel, to allow it to perform additional initialisation that requires access to the model (for example, this is where `FieldPanel` retrieves the model field definition). +* The view then calls `get_form_class` on the top-level panel to retrieve a ModelForm subclass that can be used to edit the model. This proceeds as follows: +** Retrieve a base form class from the model's `base_form_class` property, falling back on `wagtail.admin.forms.WagtailAdminModelForm` +** Call `get_form_options` on each child panel - which returns a dictionary of properties including `fields` and `widgets` - and merge the results into a single dictionary +** Construct a subclass of the base form class, with the options dict forming the attributes of the inner `Meta` class. +* An instance of the form class is created as per a normal Django form view. +* The view then calls `get_bound_panel` on the top-level panel, passing `instance`, `form` and `request` as keyword arguments. This returns a `BoundPanel` object, which follows [the template component API](/extending/template_components). Finally, the `BoundPanel` object (and its media definition) is rendered onto the template.