Fix Wagtail article publish regressions
This commit is contained in:
@@ -222,7 +222,24 @@ class ArticlePageAdminForm(WagtailAdminPageForm):
|
||||
self.initial["category"] = default_category.pk
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = getattr(self, "cleaned_data", {})
|
||||
self._apply_defaults(cleaned_data)
|
||||
self.cleaned_data = cleaned_data
|
||||
|
||||
cleaned_data = super().clean()
|
||||
self._apply_defaults(cleaned_data)
|
||||
|
||||
if not cleaned_data.get("slug"):
|
||||
self.add_error("slug", "Slug is required.")
|
||||
if not cleaned_data.get("author"):
|
||||
self.add_error("author", "Author is required.")
|
||||
if not cleaned_data.get("category"):
|
||||
self.add_error("category", "Category is required.")
|
||||
if not cleaned_data.get("summary"):
|
||||
self.add_error("summary", "Summary is required.")
|
||||
return cleaned_data
|
||||
|
||||
def _apply_defaults(self, cleaned_data: dict[str, Any]) -> dict[str, Any]:
|
||||
title = (cleaned_data.get("title") or "").strip()
|
||||
|
||||
if not cleaned_data.get("slug") and title:
|
||||
@@ -237,14 +254,6 @@ class ArticlePageAdminForm(WagtailAdminPageForm):
|
||||
max_chars=self.SUMMARY_MAX_CHARS,
|
||||
) or title
|
||||
|
||||
if not cleaned_data.get("slug"):
|
||||
self.add_error("slug", "Slug is required.")
|
||||
if not cleaned_data.get("author"):
|
||||
self.add_error("author", "Author is required.")
|
||||
if not cleaned_data.get("category"):
|
||||
self.add_error("category", "Category is required.")
|
||||
if not cleaned_data.get("summary"):
|
||||
self.add_error("summary", "Summary is required.")
|
||||
return cleaned_data
|
||||
|
||||
def _get_default_author(self, *, create: bool) -> Author | None:
|
||||
@@ -374,9 +383,20 @@ class ArticlePage(SeoMixin, Page):
|
||||
self.summary = _generate_summary_from_stream(self.body) or self.title
|
||||
if not self.published_date and self.first_published_at:
|
||||
self.published_date = self.first_published_at
|
||||
self.read_time_mins = self._compute_read_time()
|
||||
if self._should_refresh_read_time():
|
||||
self.read_time_mins = self._compute_read_time()
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
def _should_refresh_read_time(self) -> bool:
|
||||
if not self.pk:
|
||||
return True
|
||||
|
||||
previous = type(self).objects.only("body").filter(pk=self.pk).first()
|
||||
if previous is None:
|
||||
return True
|
||||
|
||||
return previous.body_text != self.body_text
|
||||
|
||||
def _compute_read_time(self) -> int:
|
||||
words = []
|
||||
for block in self.body:
|
||||
|
||||
@@ -2,6 +2,9 @@ from datetime import timedelta
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from django.contrib import messages
|
||||
from django.contrib.messages.storage.fallback import FallbackStorage
|
||||
from django.contrib.sessions.middleware import SessionMiddleware
|
||||
from django.test import override_settings
|
||||
from django.utils import timezone
|
||||
|
||||
@@ -295,7 +298,7 @@ def test_article_admin_form_relaxes_initial_required_fields(article_index, djang
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_article_admin_form_clean_applies_defaults(article_index, django_user_model, monkeypatch):
|
||||
"""Form clean should auto-fill slug/author/category/summary when omitted."""
|
||||
"""Form clean should populate defaults before parent validation runs."""
|
||||
user = django_user_model.objects.create_user(
|
||||
username="writer",
|
||||
email="writer@example.com",
|
||||
@@ -310,16 +313,18 @@ def test_article_admin_form_clean_applies_defaults(article_index, django_user_mo
|
||||
SimpleNamespace(block_type="code", value=SimpleNamespace(raw_code="print('ignore')")),
|
||||
SimpleNamespace(block_type="rich_text", value=SimpleNamespace(source="<p>Hello world body text.</p>")),
|
||||
]
|
||||
form.cleaned_data = {
|
||||
"title": "Auto Defaults Title",
|
||||
"slug": "",
|
||||
"author": None,
|
||||
"category": None,
|
||||
"summary": "",
|
||||
"body": body,
|
||||
}
|
||||
observed = {}
|
||||
|
||||
def fake_super_clean(_self):
|
||||
_self.cleaned_data = {
|
||||
"title": "Auto Defaults Title",
|
||||
"slug": "",
|
||||
"author": None,
|
||||
"category": None,
|
||||
"summary": "",
|
||||
"body": body,
|
||||
}
|
||||
observed["slug_before_parent_clean"] = _self.cleaned_data.get("slug")
|
||||
return _self.cleaned_data
|
||||
|
||||
mro = form.__class__.__mro__
|
||||
@@ -327,6 +332,7 @@ def test_article_admin_form_clean_applies_defaults(article_index, django_user_mo
|
||||
monkeypatch.setattr(super_form_class, "clean", fake_super_clean)
|
||||
cleaned = form.clean()
|
||||
|
||||
assert observed["slug_before_parent_clean"] == "auto-defaults-title"
|
||||
assert cleaned["slug"] == "auto-defaults-title"
|
||||
assert cleaned["author"] is not None
|
||||
assert cleaned["author"].user_id == user.id
|
||||
@@ -378,3 +384,20 @@ def test_article_save_autogenerates_summary_when_missing(article_index):
|
||||
article.save()
|
||||
|
||||
assert article.summary == "This should become the summary text."
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_article_page_omits_admin_messages_on_frontend(article_page, rf):
|
||||
"""Frontend templates should not render admin session messages."""
|
||||
request = rf.get(article_page.url)
|
||||
SessionMiddleware(lambda req: None).process_request(request)
|
||||
request.session.save()
|
||||
setattr(request, "_messages", FallbackStorage(request))
|
||||
messages.success(request, "Page 'Test' has been published.")
|
||||
|
||||
response = article_page.serve(request)
|
||||
response.render()
|
||||
content = response.content.decode()
|
||||
|
||||
assert "Page 'Test' has been published." not in content
|
||||
assert 'aria-label="Messages"' not in content
|
||||
|
||||
@@ -59,6 +59,32 @@ def test_article_default_category_is_assigned(home_page):
|
||||
assert article.category.slug == "general"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_article_read_time_is_not_recomputed_when_body_text_is_unchanged(home_page, monkeypatch):
|
||||
index = ArticleIndexPage(title="Articles", slug="articles")
|
||||
home_page.add_child(instance=index)
|
||||
author = AuthorFactory()
|
||||
article = ArticlePage(
|
||||
title="Stable read time",
|
||||
slug="stable-read-time",
|
||||
author=author,
|
||||
summary="s",
|
||||
body=[("rich_text", "<p>body words</p>")],
|
||||
)
|
||||
index.add_child(instance=article)
|
||||
article.save()
|
||||
|
||||
def fail_compute():
|
||||
raise AssertionError("read time should not be recomputed when body text is unchanged")
|
||||
|
||||
monkeypatch.setattr(article, "_compute_read_time", fail_compute)
|
||||
article.title = "Retitled"
|
||||
article.save()
|
||||
article.refresh_from_db()
|
||||
|
||||
assert article.read_time_mins == 1
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_category_ordering():
|
||||
Category.objects.get_or_create(name="General", slug="general")
|
||||
|
||||
Reference in New Issue
Block a user