Allow StreamField blocks to be edited via your Wagtail site's frontend.
wagtail-liveedit-demo.mp4
- Wagtail (see supported versions below)
- Python 3.8+
| Wagtail version | Passing tests? |
|---|---|
| 7.2.1 | ✔️ |
| 7.1.2 | ✔️ |
| 7.0.3 | ✔️ |
| 6.4.2 | ✔️ |
| 6.3.5 | ✔️ |
| 6.2.4 | ✔️ |
| 6.1.3 | ✔️ |
| 6.0.6 | ✔️ |
| 5.2.8 | ✔️ |
-
Run
pip install wagtail-liveedit -
Add
'liveedit'to INSTALLED_APPS. -
Add:
url(r'^__liveedit__/', include('liveedit.urls')),
to your app's
urls.pyurlpatterns list -
In your templates, when rendering StreamFields, replace:
{% for block in page.body %} {% include_block block %} {% endfor %}with
{% load liveedit %} {% for block in page.body %} {% liveedit_include_block block object=page field="body" %} {% empty %} {% liveedit_insert_new object=page field="body" %} {% endfor %}where
pageis the model instance to which the StreamField belongs, and"body"is the name of the StreamField on that model instance. -
In your block templates, add
{% liveedit_attributes %}inside the outermost opening HTML tag. For example:{% load liveedit %} <div class="block-text" {% liveedit_attributes %}> ... </div> -
In your base template add:
{% liveedit_css %}with the style tags inside the
<head>and,{% liveedit_js %}just before the closing
</body>tag.(Also add
{% load liveedit %}at the top).
You can also make a StreamField block editable by supplying parameters to the
liveedit_attributes template tag directly:
{% for block in page.body %}
<div {% liveedit_attributes block object=page field="body" %}>
{{ block.value.text }}
</div>
{% endfor %}-
wagtail-liveedit is dependent on various Wagtail internals, such as the admin editor views and StreamValue methods. For this reason, this package's Wagtail version compatibility will only be updated after it has been tested against each individual Wagtail version. -
Be careful if caching your rendered pages, since:
- you don't want anonymous visitors seeing the liveedit controls (even though they won't have permission to alter anything)
- you will want admins to see their changes reflected immediately
The easiest approach is to disable caching of rendered pages if the user is logged in, which can be done with middleware:
def cache_helper_middleware(get_response): def middleware(request): response = get_response(request) if request.user.is_authenticated: response['Cache-Control'] = 'no-cache' return response return middleware
-
To avoid every block edit resulting in a new page revision being created,
wagtail-liveeditchecks the age of the current revision, and the logged-in user. If the current revision was created over an hour ago, or the current revision was created by a different user, then editing a block will result in a new revision being published, otherwise the existing one will be modified.This effectively merges together all block edits made by a single user, within an hour of each other, into a single page revision.
-
wagtail-liveeditinserts an extra<div>tag for the editor controls at the beginning of each block. This can cause styling problems, if you are using:first-childselectors to match the content of blocks, as the expected first child will become the second. You can work around this by adding an extra selector:.my-block > div:first-child, .my-block > .liveedit-bar:first-child + div {
When you call {% liveedit_include_block ... %} to render the blocks in your
templates, extra data is passed through to the block template (via the context).
When you then use {% liveedit_attributes %} in your block template, if you
have sufficient permissions, this data is output as JSON within a custom
data-liveedit HTML attribute.
This extra data consists of:
- The content type of the model that the StreamField is on.
- The id of the model instance.
- The name of the StreamField on the model.
- The id of the block itself.
The frontend Javascript runs on page load, finds the blocks that have the
data-liveedit attribute, and adds interactive editor controls. If you click
the controls to edit a block, an iframe is created to load a form via the
backend API, populated with the indicated StreamField's current value. When you
then save the form, the backend finds the block within the indicated
StreamField, updates it, and resaves the model.