Address PR review feedback
- 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:
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user