Address PR review feedback
Some checks failed
CI / nightly-e2e (pull_request) Has been skipped
CI / deploy (pull_request) Has been skipped
CI / ci (pull_request) Successful in 1m19s
CI / pr-e2e (pull_request) Failing after 2m13s

- Gate e2e-admin superuser behind E2E_MODE env var (security)
- Add status and tag filters to ArticleFilterSet
- Set default_ordering to -published_date on listing viewset
- Add summary to ArticlePage.search_fields for search support
- Add 4 new tests for filters, ordering, and search fields

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Mark
2026-03-03 20:37:58 +00:00
parent d387bf4f03
commit b897447296
5 changed files with 78 additions and 3 deletions

View File

@@ -15,6 +15,7 @@ from wagtail.admin.panels import FieldPanel, ObjectList, PageChooserPanel, Tabbe
from wagtail.contrib.routable_page.models import RoutablePageMixin, route from wagtail.contrib.routable_page.models import RoutablePageMixin, route
from wagtail.fields import RichTextField, StreamField from wagtail.fields import RichTextField, StreamField
from wagtail.models import Page from wagtail.models import Page
from wagtail.search import index
from wagtailseo.models import SeoMixin from wagtailseo.models import SeoMixin
from apps.blog.blocks import ARTICLE_BODY_BLOCKS 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: def save(self, *args: Any, **kwargs: Any) -> None:
if not self.category_id: if not self.category_id:

View File

@@ -232,3 +232,44 @@ def test_article_edit_page_has_tabbed_interface(client, django_user_model, home_
assert "Metadata" in content assert "Metadata" in content
assert "Publishing" in content assert "Publishing" in content
assert "SEO" 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

View File

@@ -1,4 +1,5 @@
import django_filters import django_filters
from taggit.models import Tag
from wagtail import hooks from wagtail import hooks
from wagtail.admin.filters import WagtailFilterSet from wagtail.admin.filters import WagtailFilterSet
from wagtail.admin.ui.components import Component 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.authors.models import Author
from apps.blog.models import ArticlePage, Category, TagMetadata from apps.blog.models import ArticlePage, Category, TagMetadata
STATUS_CHOICES = [
("live", "Published"),
("draft", "Draft"),
("scheduled", "Scheduled"),
]
class TagMetadataViewSet(SnippetViewSet): class TagMetadataViewSet(SnippetViewSet):
model = TagMetadata model = TagMetadata
@@ -35,6 +42,17 @@ register_snippet(CategoryViewSet)
# ── Articles page listing ──────────────────────────────────────────────────── # ── 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): class ArticleFilterSet(WagtailFilterSet):
category = django_filters.ModelChoiceFilter( category = django_filters.ModelChoiceFilter(
queryset=Category.objects.all(), queryset=Category.objects.all(),
@@ -44,6 +62,15 @@ class ArticleFilterSet(WagtailFilterSet):
queryset=Author.objects.all(), queryset=Author.objects.all(),
empty_label="All authors", 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: class Meta:
model = ArticlePage model = ArticlePage
@@ -66,6 +93,7 @@ class ArticlePageListingViewSet(PageListingViewSet):
PageStatusColumn("status", sort_key="live"), PageStatusColumn("status", sort_key="live"),
] ]
filterset_class = ArticleFilterSet filterset_class = ArticleFilterSet
default_ordering = "-published_date"
@hooks.register("register_admin_viewset") @hooks.register("register_admin_viewset")

View File

@@ -1,5 +1,7 @@
from __future__ import annotations from __future__ import annotations
import os
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from taggit.models import Tag from taggit.models import Tag
@@ -200,8 +202,8 @@ class Command(BaseCommand):
] ]
) )
# Admin user for E2E admin tests # Admin user for E2E admin tests — only when E2E_MODE is set
if not User.objects.filter(username="e2e-admin").exists(): if os.environ.get("E2E_MODE") and not User.objects.filter(username="e2e-admin").exists():
User.objects.create_superuser( User.objects.create_superuser(
username="e2e-admin", username="e2e-admin",
email="e2e-admin@example.com", email="e2e-admin@example.com",

View File

@@ -24,6 +24,7 @@ services:
EMAIL_BACKEND: django.core.mail.backends.console.EmailBackend EMAIL_BACKEND: django.core.mail.backends.console.EmailBackend
DEFAULT_FROM_EMAIL: hello@nohypeai.com DEFAULT_FROM_EMAIL: hello@nohypeai.com
NEWSLETTER_PROVIDER: buttondown NEWSLETTER_PROVIDER: buttondown
E2E_MODE: "1"
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy