kopia lustrzana https://github.com/wagtail/wagtail
Relax model validation when saving pages as draft
rodzic
13dbf61323
commit
1f210831dd
|
@ -113,6 +113,7 @@ def clear_page_url_from_cache_on_move(sender, **kwargs):
|
|||
pre_page_move.connect(clear_old_page_urls_from_cache)
|
||||
```
|
||||
|
||||
(page_slug_changed)=
|
||||
## `page_slug_changed`
|
||||
|
||||
This signal is emitted from a `Page` when a change to its slug is published.
|
||||
|
|
|
@ -54,6 +54,10 @@ depth: 1
|
|||
|
||||
## Upgrade considerations - changes affecting all projects
|
||||
|
||||
### `Page.save()` no longer automatically calls `full_clean` for draft pages
|
||||
|
||||
In previous releases, the `save()` method on page models called the `full_clean` method to apply [model-level validation rules](inv:django#validating-objects), regardless of whether the page was in a draft or live state, unless this was explicitly disabled by passing `clean=False`. As of this release, saving a page in a draft state (`live=False`) will only perform the minimum validation necessary to ensure data integrity: the title must be non-empty, and the slug must be unique within the parent page. Saving a page with `live=True` will apply full validation as before. If you have user code that creates draft pages and requires them to be validated, you must now call `full_clean` explicitly.
|
||||
|
||||
## Upgrade considerations - deprecation of old functionality
|
||||
|
||||
### `TAG_LIMIT` and `TAG_SPACES_ALLOWED` settings renamed to `WAGTAIL_TAG_LIMIT` and `WAGTAIL_TAG_SPACES_ALLOWED`
|
||||
|
|
|
@ -650,8 +650,7 @@ class Page(AbstractPage, index.Indexed, ClusterableModel, metaclass=PageBase):
|
|||
self._set_core_field_defaults()
|
||||
super().full_clean(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
def _check_slug_is_unique(self):
|
||||
parent_page = self.get_parent()
|
||||
if not Page._slug_is_available(self.slug, parent_page, self):
|
||||
raise ValidationError(
|
||||
|
@ -663,6 +662,15 @@ class Page(AbstractPage, index.Indexed, ClusterableModel, metaclass=PageBase):
|
|||
}
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
self._check_slug_is_unique()
|
||||
|
||||
def minimal_clean(self):
|
||||
self._set_core_field_defaults()
|
||||
self.title = self._meta.get_field("title").clean(self.title, self)
|
||||
self._check_slug_is_unique()
|
||||
|
||||
def is_site_root(self):
|
||||
"""
|
||||
Returns True if this page is the root of any site.
|
||||
|
@ -682,25 +690,35 @@ class Page(AbstractPage, index.Indexed, ClusterableModel, metaclass=PageBase):
|
|||
# ensure that changes are only committed when we have updated all descendant URL paths, to preserve consistency
|
||||
def save(self, clean=True, user=None, log_action=False, **kwargs):
|
||||
"""
|
||||
Overrides default method behavior to make additional updates unique to pages,
|
||||
such as updating the ``url_path`` value of descendant page to reflect changes
|
||||
to this page's slug.
|
||||
Writes the page to the database, performing additional housekeeping tasks to ensure data
|
||||
integrity:
|
||||
|
||||
New pages should generally be saved via the :meth:`~treebeard.mp_tree.MP_Node.add_child`
|
||||
or :meth:`~treebeard.mp_tree.MP_Node.add_sibling`
|
||||
method of an existing page, which will correctly set the ``path`` and ``depth``
|
||||
fields on the new page before saving it.
|
||||
* ``locale``, ``draft_title`` and ``slug`` are set to default values if not provided, with ``slug``
|
||||
being generated from the title with a suffix to ensure uniqueness within the parent page
|
||||
where necessary
|
||||
* The ``url_path`` field is set based on the ``slug`` and the parent page
|
||||
* If the ``slug`` has changed, the ``url_path`` of this page and all descendants is updated and
|
||||
a :ref:`page_slug_changed` signal is sent
|
||||
|
||||
By default, pages are validated using ``full_clean()`` before attempting to
|
||||
save changes to the database, which helps to preserve validity when restoring
|
||||
pages from historic revisions (which might not necessarily reflect the current
|
||||
model state). This validation step can be bypassed by calling the method with
|
||||
``clean=False``.
|
||||
New pages should be saved by passing the unsaved page instance to the
|
||||
:meth:`~treebeard.mp_tree.MP_Node.add_child`
|
||||
or :meth:`~treebeard.mp_tree.MP_Node.add_sibling` method of an existing page, which will correctly update
|
||||
the fields responsible for tracking the page's location in the tree.
|
||||
|
||||
If ``clean=False`` is passed, the page is saved without validation. This is appropriate for updates that only
|
||||
change metadata such as `latest_revision` while keeping content and page location unchanged.
|
||||
|
||||
If ``clean=True`` is passed (the default), and the page has ``live=True`` set, the page is validated using
|
||||
:meth:`~django.db.models.Model.full_clean` before saving.
|
||||
|
||||
If ``clean=True`` is passed, and the page has `live=False` set, only the title and slug fields are validated.
|
||||
"""
|
||||
if clean:
|
||||
self.full_clean()
|
||||
else:
|
||||
self._set_core_field_defaults()
|
||||
if self.live:
|
||||
self.full_clean()
|
||||
else:
|
||||
# Saving as draft; only perform the minimal validation to satisfy data integrity
|
||||
self.minimal_clean()
|
||||
|
||||
slug_changed = False
|
||||
is_new = self.id is None
|
||||
|
|
|
@ -349,7 +349,7 @@ class TestPublishScheduledPagesCommand(WagtailTestUtils, TestCase):
|
|||
.exists()
|
||||
)
|
||||
|
||||
with self.assertNumQueries(47):
|
||||
with self.assertNumQueries(42):
|
||||
with self.captureOnCommitCallbacks(execute=True):
|
||||
management.call_command("publish_scheduled_pages")
|
||||
|
||||
|
|
|
@ -104,6 +104,19 @@ class TestValidation(TestCase):
|
|||
with self.assertRaises(ValidationError):
|
||||
homepage.add_child(instance=hello_page)
|
||||
|
||||
def test_title_is_required_for_draft_page(self):
|
||||
homepage = Page.objects.get(url_path="/home/")
|
||||
|
||||
hello_page = SimplePage(slug="hello-world", content="hello", live=False)
|
||||
with self.assertRaises(ValidationError):
|
||||
homepage.add_child(instance=hello_page)
|
||||
|
||||
hello_page = SimplePage(
|
||||
title="", slug="hello-world", content="hello", live=False
|
||||
)
|
||||
with self.assertRaises(ValidationError):
|
||||
homepage.add_child(instance=hello_page)
|
||||
|
||||
def test_slug_is_autogenerated(self):
|
||||
homepage = Page.objects.get(url_path="/home/")
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue