feat: improve Wagtail admin editor experience for articles #40
@@ -15,6 +15,7 @@ from wagtail.admin.panels import FieldPanel, ObjectList, PageChooserPanel, Tabbe
|
||||
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
|
||||
from wagtail.fields import RichTextField, StreamField
|
||||
from wagtail.models import Page
|
||||
from wagtail.search import index
|
||||
from wagtailseo.models import SeoMixin
|
||||
|
||||
from apps.blog.blocks import ARTICLE_BODY_BLOCKS
|
||||
@@ -232,7 +233,9 @@ class ArticlePage(SeoMixin, Page):
|
||||
]
|
||||
)
|
||||
|
||||
search_fields = Page.search_fields
|
||||
search_fields = Page.search_fields + [
|
||||
index.SearchField("summary"),
|
||||
]
|
||||
|
||||
def save(self, *args: Any, **kwargs: Any) -> None:
|
||||
if not self.category_id:
|
||||
|
||||
@@ -232,3 +232,44 @@ def test_article_edit_page_has_tabbed_interface(client, django_user_model, home_
|
||||
assert "Metadata" in content
|
||||
assert "Publishing" in content
|
||||
assert "SEO" in content
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(ALLOWED_HOSTS=["testserver", "localhost", "127.0.0.1"])
|
||||
def test_articles_listing_has_status_filter(client, django_user_model, home_page):
|
||||
"""The Articles listing should accept status filter parameter."""
|
||||
admin = django_user_model.objects.create_superuser(
|
||||
username="admin", email="admin@example.com", password="admin-pass"
|
||||
)
|
||||
client.force_login(admin)
|
||||
response = client.get("/cms/articles/?status=live")
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(ALLOWED_HOSTS=["testserver", "localhost", "127.0.0.1"])
|
||||
def test_articles_listing_has_tag_filter(client, django_user_model, home_page):
|
||||
"""The Articles listing should accept tag filter parameter."""
|
||||
admin = django_user_model.objects.create_superuser(
|
||||
username="admin", email="admin@example.com", password="admin-pass"
|
||||
)
|
||||
client.force_login(admin)
|
||||
response = client.get("/cms/articles/?tag=1")
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_article_listing_default_ordering():
|
||||
"""ArticlePageListingViewSet should default to -published_date ordering."""
|
||||
from apps.blog.wagtail_hooks import ArticlePageListingViewSet
|
||||
|
||||
assert ArticlePageListingViewSet.default_ordering == "-published_date"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_article_search_fields_include_summary():
|
||||
"""ArticlePage.search_fields should index the summary field."""
|
||||
field_names = [
|
||||
f.field_name for f in ArticlePage.search_fields if hasattr(f, "field_name")
|
||||
]
|
||||
assert "summary" in field_names
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import django_filters
|
||||
from taggit.models import Tag
|
||||
from wagtail import hooks
|
||||
from wagtail.admin.filters import WagtailFilterSet
|
||||
from wagtail.admin.ui.components import Component
|
||||
@@ -11,6 +12,12 @@ from wagtail.snippets.views.snippets import SnippetViewSet
|
||||
from apps.authors.models import Author
|
||||
from apps.blog.models import ArticlePage, Category, TagMetadata
|
||||
|
||||
STATUS_CHOICES = [
|
||||
("live", "Published"),
|
||||
("draft", "Draft"),
|
||||
("scheduled", "Scheduled"),
|
||||
]
|
||||
|
||||
|
||||
class TagMetadataViewSet(SnippetViewSet):
|
||||
model = TagMetadata
|
||||
@@ -35,6 +42,17 @@ register_snippet(CategoryViewSet)
|
||||
# ── Articles page listing ────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class StatusFilter(django_filters.ChoiceFilter):
|
||||
def filter(self, qs, value): # noqa: A003
|
||||
if value == "live":
|
||||
return qs.filter(live=True)
|
||||
if value == "draft":
|
||||
return qs.filter(live=False, go_live_at__isnull=True)
|
||||
if value == "scheduled":
|
||||
return qs.filter(live=False, go_live_at__isnull=False)
|
||||
return qs
|
||||
|
||||
|
||||
class ArticleFilterSet(WagtailFilterSet):
|
||||
category = django_filters.ModelChoiceFilter(
|
||||
queryset=Category.objects.all(),
|
||||
@@ -44,6 +62,15 @@ class ArticleFilterSet(WagtailFilterSet):
|
||||
queryset=Author.objects.all(),
|
||||
empty_label="All authors",
|
||||
)
|
||||
status = StatusFilter(
|
||||
choices=STATUS_CHOICES,
|
||||
empty_label="All statuses",
|
||||
)
|
||||
tag = django_filters.ModelChoiceFilter(
|
||||
field_name="tags",
|
||||
queryset=Tag.objects.all(),
|
||||
empty_label="All tags",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ArticlePage
|
||||
@@ -66,6 +93,7 @@ class ArticlePageListingViewSet(PageListingViewSet):
|
||||
PageStatusColumn("status", sort_key="live"),
|
||||
]
|
||||
filterset_class = ArticleFilterSet
|
||||
default_ordering = "-published_date"
|
||||
|
||||
|
||||
@hooks.register("register_admin_viewset")
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.management.base import BaseCommand
|
||||
from taggit.models import Tag
|
||||
@@ -200,8 +202,8 @@ class Command(BaseCommand):
|
||||
]
|
||||
)
|
||||
|
||||
# Admin user for E2E admin tests
|
||||
if not User.objects.filter(username="e2e-admin").exists():
|
||||
# Admin user for E2E admin tests — only when E2E_MODE is set
|
||||
if os.environ.get("E2E_MODE") and not User.objects.filter(username="e2e-admin").exists():
|
||||
User.objects.create_superuser(
|
||||
username="e2e-admin",
|
||||
email="e2e-admin@example.com",
|
||||
|
||||
@@ -24,6 +24,7 @@ services:
|
||||
EMAIL_BACKEND: django.core.mail.backends.console.EmailBackend
|
||||
DEFAULT_FROM_EMAIL: hello@nohypeai.com
|
||||
NEWSLETTER_PROVIDER: buttondown
|
||||
E2E_MODE: "1"
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
Reference in New Issue
Block a user