Makes the page tree a bit more messy, but is much more versatile and means there are fewer hacks in the code to make snippets act like pages in the tree.
111 lines
3.8 KiB
Python
111 lines
3.8 KiB
Python
from typing import Any
|
|
|
|
from django.db import models
|
|
from django.db.models.functions import TruncMonth
|
|
from django.http.request import HttpRequest
|
|
from django.utils import timezone
|
|
from django.utils.functional import cached_property
|
|
from modelcluster.fields import ParentalManyToManyField
|
|
from wagtail.admin.panels import FieldPanel
|
|
from wagtail.query import PageQuerySet
|
|
|
|
from website.common.models import BaseContentMixin, BasePage
|
|
from website.common.utils import TocEntry
|
|
|
|
|
|
class BlogListPage(BaseContentMixin, BasePage): # type: ignore[misc]
|
|
max_count = 1
|
|
subpage_types = ["blog.BlogPostPage", "blog.BlogPostTagListPage"]
|
|
content_panels = BasePage.content_panels + BaseContentMixin.content_panels
|
|
|
|
@cached_property
|
|
def reading_time(self) -> int:
|
|
"""
|
|
How does one read a list page?
|
|
"""
|
|
return 0
|
|
|
|
@cached_property
|
|
def table_of_contents(self) -> list[TocEntry]:
|
|
post_months = [
|
|
dt.strftime("%Y-%m")
|
|
for dt in self.get_blog_posts()
|
|
.annotate(post_month=TruncMonth("date", output_field=models.DateField()))
|
|
.order_by("-post_month")
|
|
.values_list("post_month", flat=True)
|
|
.distinct()
|
|
]
|
|
|
|
return [TocEntry(post_month, post_month, 0, []) for post_month in post_months]
|
|
|
|
def get_blog_posts(self) -> PageQuerySet:
|
|
return BlogPostPage.objects.child_of(self).live() # type:ignore[attr-defined]
|
|
|
|
def get_context(self, request: HttpRequest) -> dict:
|
|
context = super().get_context(request)
|
|
context["child_pages"] = (
|
|
self.get_blog_posts()
|
|
.select_related("hero_image")
|
|
.select_related("hero_unsplash_photo")
|
|
.prefetch_related("tags")
|
|
.order_by("-date")
|
|
)
|
|
return context
|
|
|
|
|
|
class BlogPostPage(BaseContentMixin, BasePage): # type: ignore[misc]
|
|
subpage_types: list[Any] = []
|
|
parent_page_types = [BlogListPage]
|
|
|
|
tags = ParentalManyToManyField("blog.BlogPostTagPage", blank=True)
|
|
date = models.DateField(default=timezone.now)
|
|
|
|
content_panels = (
|
|
BasePage.content_panels
|
|
+ BaseContentMixin.content_panels
|
|
+ [FieldPanel("date"), FieldPanel("tags")]
|
|
)
|
|
|
|
|
|
class BlogPostTagListPage(BaseContentMixin, BasePage): # type: ignore[misc]
|
|
max_count = 1
|
|
parent_page_types = [BlogListPage]
|
|
subpage_types = ["blog.BlogPostTagPage"]
|
|
|
|
content_panels = BasePage.content_panels + BaseContentMixin.content_panels
|
|
|
|
@cached_property
|
|
def table_of_contents(self) -> list[TocEntry]:
|
|
return [TocEntry(page.title, page.slug, 0, []) for page in self.get_tags()]
|
|
|
|
def get_tags(self) -> PageQuerySet:
|
|
return self.get_children().specific().live().order_by("title")
|
|
|
|
def get_context(self, request: HttpRequest) -> dict:
|
|
context = super().get_context(request)
|
|
context["tags"] = self.get_children().specific().live().order_by("title")
|
|
return context
|
|
|
|
|
|
class BlogPostTagPage(BaseContentMixin, BasePage): # type: ignore[misc]
|
|
subpage_types: list[Any] = []
|
|
parent_page_types = [BlogPostTagListPage]
|
|
|
|
content_panels = BasePage.content_panels + BaseContentMixin.content_panels
|
|
|
|
@cached_property
|
|
def table_of_contents(self) -> list[TocEntry]:
|
|
return [
|
|
TocEntry(page.title, page.slug, 0, []) for page in self.get_blog_posts()
|
|
]
|
|
|
|
def get_blog_posts(self) -> PageQuerySet:
|
|
blog_list_page = self.get_parent_pages().specific().reverse()[1]
|
|
assert isinstance(blog_list_page, BlogListPage)
|
|
return blog_list_page.get_blog_posts().filter(tags=self).order_by("-date")
|
|
|
|
def get_context(self, request: HttpRequest) -> dict:
|
|
context = super().get_context(request)
|
|
context["pages"] = self.get_blog_posts()
|
|
return context
|