Fix empty-category nav and route behavior
Some checks failed
CI / nightly-e2e (pull_request) Has been skipped
CI / deploy (pull_request) Has been skipped
CI / ci (pull_request) Successful in 1m17s
CI / pr-e2e (pull_request) Failing after 44s

Use category-state-driven queries for nav and category listing routes, and add regression tests for empty but valid categories.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Mark
2026-03-03 12:39:47 +00:00
parent f7ca4bc44b
commit 04a55844fd
4 changed files with 27 additions and 14 deletions

View File

@@ -49,10 +49,7 @@ class HomePage(Page):
id__in=ArticlePage.objects.live().public().values_list("tags__id", flat=True) id__in=ArticlePage.objects.live().public().values_list("tags__id", flat=True)
).distinct().order_by("name") ).distinct().order_by("name")
) )
category_ids = ArticlePage.objects.live().public().values_list("category_id", flat=True) ctx["available_categories"] = Category.objects.filter(show_in_nav=True).order_by("sort_order", "name")
ctx["available_categories"] = Category.objects.filter(show_in_nav=True, id__in=category_ids).order_by(
"sort_order", "name"
)
return ctx return ctx
@@ -76,9 +73,7 @@ class ArticleIndexPage(RoutablePageMixin, Page):
def get_listing_context(self, request, active_category=None): def get_listing_context(self, request, active_category=None):
tag_slug = request.GET.get("tag") tag_slug = request.GET.get("tag")
articles = self.get_articles() articles = self.get_articles()
all_articles = articles available_categories = Category.objects.order_by("sort_order", "name")
category_ids = all_articles.values_list("category_id", flat=True)
available_categories = Category.objects.filter(id__in=category_ids).order_by("sort_order", "name")
category_links = [ category_links = [
{"category": category, "url": self.get_category_url(category)} {"category": category, "url": self.get_category_url(category)}
for category in available_categories for category in available_categories
@@ -111,9 +106,7 @@ class ArticleIndexPage(RoutablePageMixin, Page):
@route(r"^category/(?P<category_slug>[-\w]+)/$") @route(r"^category/(?P<category_slug>[-\w]+)/$")
def category_listing(self, request, category_slug): def category_listing(self, request, category_slug):
category_ids = self.get_articles().values_list("category_id", flat=True) category = get_object_or_404(Category, slug=category_slug)
category_qs = Category.objects.filter(id__in=category_ids)
category = get_object_or_404(category_qs, slug=category_slug)
return self.render(request, context_overrides=self.get_listing_context(request, active_category=category)) return self.render(request, context_overrides=self.get_listing_context(request, active_category=category))
def get_context(self, request, *args, **kwargs): def get_context(self, request, *args, **kwargs):

View File

@@ -233,3 +233,14 @@ def test_article_index_category_route_supports_tag_filter(client, home_page):
assert resp.status_code == 200 assert resp.status_code == 200
assert "Keep Me" in html assert "Keep Me" in html
assert "Drop Me" not in html assert "Drop Me" not in html
@pytest.mark.django_db
def test_article_index_category_route_allows_empty_existing_category(client, home_page):
index = ArticleIndexPage(title="Articles", slug="articles")
home_page.add_child(instance=index)
Category.objects.create(name="Opinion", slug="opinion")
resp = client.get("/articles/category/opinion/")
assert resp.status_code == 200
assert "No articles found." in resp.content.decode()

View File

@@ -4,7 +4,7 @@ from django import template
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from wagtail.models import Site from wagtail.models import Site
from apps.blog.models import ArticleIndexPage, TagMetadata from apps.blog.models import ArticleIndexPage, Category, TagMetadata
from apps.core.models import SiteSettings from apps.core.models import SiteSettings
from apps.legal.models import LegalPage from apps.legal.models import LegalPage
@@ -58,9 +58,7 @@ def get_categories_nav(context):
index_page = index_qs.first() index_page = index_qs.first()
if not index_page: if not index_page:
return [] return []
categories = index_page.get_listing_context( categories = Category.objects.filter(show_in_nav=True).order_by("sort_order", "name")
request, active_category=None
)["available_categories"].filter(show_in_nav=True)
return [ return [
{ {
"name": category.name, "name": category.name,

View File

@@ -37,3 +37,14 @@ def test_categories_nav_tag_renders_category_link(client, home_page):
resp = client.get("/") resp = client.get("/")
assert resp.status_code == 200 assert resp.status_code == 200
assert "/articles/category/reviews/" in resp.content.decode() assert "/articles/category/reviews/" in resp.content.decode()
@pytest.mark.django_db
def test_categories_nav_tag_includes_empty_nav_category(client, home_page):
index = ArticleIndexPage(title="Articles", slug="articles")
home_page.add_child(instance=index)
Category.objects.create(name="Benchmarks", slug="benchmarks", show_in_nav=True)
resp = client.get("/")
assert resp.status_code == 200
assert "/articles/category/benchmarks/" in resp.content.decode()