Skip to content

Defer validation of required text fields until publish time#12923

Merged
gasman merged 20 commits intowagtail:mainfrom
gasman:feature/validate-on-publish-3
Mar 19, 2025
Merged

Defer validation of required text fields until publish time#12923
gasman merged 20 commits intowagtail:mainfrom
gasman:feature/validate-on-publish-3

Conversation

@gasman
Copy link
Copy Markdown
Contributor

@gasman gasman commented Feb 25, 2025

Implements RFC 104 for required fields on page and snippet models.

@gasman gasman added the type:Enhancement New features, feature requests, improvements to existing functionality, enhancements label Feb 25, 2025
@gasman gasman added this to the 6.5* milestone Feb 25, 2025
@gasman gasman marked this pull request as draft February 25, 2025 13:23
@squash-labs
Copy link
Copy Markdown

squash-labs bot commented Feb 25, 2025

Manage this branch in Squash

Test this branch here: https://featurevalidate-on-publish-3-97bsx.squash.io

@gasman gasman force-pushed the feature/validate-on-publish-3 branch from ce5b027 to 1fda901 Compare March 5, 2025 17:16
@gasman gasman self-assigned this Mar 5, 2025
@gasman gasman marked this pull request as ready for review March 5, 2025 18:04
@gasman gasman requested a review from laymonage March 5, 2025 18:04
@lb- lb- added the status:Needs Review Ready for review by maintainers, awaiting code review, pending feedback label Mar 9, 2025
Copy link
Copy Markdown
Member

@laymonage laymonage left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested it and it works very well! Just a few non-blocking comments, otherwise it looks good to me, thanks!

Comment thread wagtail/admin/ui/tables/__init__.py Outdated
def get_cell_context_data(self, instance, parent_context):
context = super().get_cell_context_data(instance, parent_context)
if not str(context["value"]).strip():
context["value"] = "(blank)"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to go with this for now, but I wonder if the columns should support something similar to ModelAdmin's empty_value_display more generally?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a good idea! Added in 453ef62 (and made translatable along the way).

Comment on lines +61 to +64
# If required_on_save is not explicitly set, treat it as false unless:
# - it corresponds to a model field with required_on_save=True (such as page title)
# - it corresponds to a non-null, non-text-typed model field (in which case a blank value
# is not valid at the database level)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me!

Comment on lines +537 to +543
@property
def action_name(self):
return self.action_name_and_method[0]

@property
def action_method(self):
return self.action_name_and_method[1]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, I've been thinking of doing some kind of mapping between the action name and method, but I think this works just as well!

Comment thread wagtail/admin/utils.py Outdated
Comment on lines +39 to +40
if result.strip() == "":
result = f"{obj.__class__.__name__} object ({obj.pk})"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this (and the other fallbacks, i.e. in audit log and table column) be translatable? Django's isn't, but we might want to do so as it's user-facing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made translatable in a70e582.

if self.form.is_valid():
return self.form_valid(self.form)
else:
self.form.restore_required_fields()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch!

Comment thread wagtail/models/pages.py Outdated
Comment on lines +709 to +715
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.

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``.
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.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we ever did it inside docstrings, but does the change warrant a versionchanged admonition? If we don't want it in the docstring, maybe we can add it below the .. automethod:: save in the Markdown file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that can't hurt... now added in e3f674a.

Copy link
Copy Markdown
Member

@laymonage laymonage left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One non-blocking comment about empty_value_display, but otherwise this looks good to me. Thanks!

value = unlocalize(value)
context["value"] = value

if not str(value).strip():
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just want to note that Django's logic is as follows:

If the value of a field is None, an empty string, or an iterable without elements, Django will display - (a dash). You can override this with AdminSite.empty_value_display:

https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display

I think taking None into consideration so that users can override empty_value_display to customise its display would be a common use case. However, we might want to change the default value to a dash like Django, as otherwise it would display as an empty string.

It's not in the scope of this PR though, but I thought I'd raise this since we're just about to add the support. Also happy to do this in a separate PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, since this is a potential change of behaviour on existing listings, i think that's best left for a separate PR.

gasman added 18 commits March 19, 2025 13:33
… from_date field

The fix to EventPageForm.clean addresses a logic bug that existed before the change to validation - if `date_from` was left blank, it would be omitted from `cleaned_data` and so retrieving it would raise an uncaught KeyError.
It is clear from the test models and bakerydemo that title is frequently declared with a plain FieldPanel rather than TitleFieldPanel, so we should not rely on the presence of TitleFieldPanel or an explicit required_on_save flag on the FieldPanel to enforce form-level validation on the title.
…r scheduling

As per wagtail/rfcs#104 (comment) - unless clean=False is explicitly passed, validation should be applied, including on fields that would accept nulls at the database level.
…ests for creating/editing invalid draft snippets
…lank

This surfaces an issue with SearchPromotion having a broken string representation when the query string is None, so fix that too
@gasman gasman force-pushed the feature/validate-on-publish-3 branch from e3f674a to 0df0a20 Compare March 19, 2025 13:34
@gasman gasman merged commit c38db51 into wagtail:main Mar 19, 2025
17 of 19 checks passed
@lb- lb- removed the status:Needs Review Ready for review by maintainers, awaiting code review, pending feedback label Mar 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:Enhancement New features, feature requests, improvements to existing functionality, enhancements

Projects

Archived in project
Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

3 participants