website/website/blog/models.py
Jake Howard 690095ea52
Replace tag snippets with full pages
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.
2022-07-16 10:29:01 +01:00

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