Move listing functionality to generic base page
This commit is contained in:
parent
655d3a484e
commit
e19a2456e7
12 changed files with 153 additions and 200 deletions
|
@ -1,25 +1,18 @@
|
||||||
from typing import Any
|
from typing import Any, Type
|
||||||
|
|
||||||
from django.core.paginator import EmptyPage
|
|
||||||
from django.core.paginator import Page as PaginatorPage
|
|
||||||
from django.core.paginator import Paginator
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.functions import TruncMonth
|
from django.db.models.functions import TruncMonth
|
||||||
from django.http.request import HttpRequest
|
|
||||||
from django.http.response import Http404, HttpResponse, HttpResponseBadRequest
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from modelcluster.fields import ParentalManyToManyField
|
from modelcluster.fields import ParentalManyToManyField
|
||||||
from wagtail.admin.panels import FieldPanel
|
from wagtail.admin.panels import FieldPanel
|
||||||
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
|
|
||||||
from wagtail.query import PageQuerySet
|
|
||||||
|
|
||||||
from website.common.models import BaseContentPage
|
from website.common.models import BaseContentPage, BaseListingPage
|
||||||
from website.common.serializers import PaginationSerializer
|
|
||||||
from website.common.utils import TocEntry
|
from website.common.utils import TocEntry
|
||||||
|
from website.common.views import ContentPageFeed
|
||||||
|
|
||||||
|
|
||||||
class BlogPostListPage(RoutablePageMixin, BaseContentPage):
|
class BlogPostListPage(BaseListingPage):
|
||||||
max_count = 1
|
max_count = 1
|
||||||
subpage_types = [
|
subpage_types = [
|
||||||
"blog.BlogPostPage",
|
"blog.BlogPostPage",
|
||||||
|
@ -28,60 +21,36 @@ class BlogPostListPage(RoutablePageMixin, BaseContentPage):
|
||||||
"blog.BlogPostCollectionPage",
|
"blog.BlogPostCollectionPage",
|
||||||
]
|
]
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def show_reading_time(self) -> bool:
|
|
||||||
return False
|
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def table_of_contents(self) -> list[TocEntry]:
|
def table_of_contents(self) -> list[TocEntry]:
|
||||||
post_months = sorted(
|
post_months = sorted(
|
||||||
{
|
{
|
||||||
dt.strftime("%Y-%m")
|
dt.strftime("%Y-%m")
|
||||||
for dt in self.paginator_page.object_list.annotate(
|
for dt in self.get_paginator_page()
|
||||||
|
.object_list.annotate(
|
||||||
post_month=TruncMonth("date", output_field=models.DateField())
|
post_month=TruncMonth("date", output_field=models.DateField())
|
||||||
).values_list("post_month", flat=True)
|
)
|
||||||
|
.values_list("post_month", flat=True)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return [TocEntry(post_month, post_month, 0, []) for post_month in post_months]
|
return [TocEntry(post_month, post_month, 0, []) for post_month in post_months]
|
||||||
|
|
||||||
def get_blog_posts(self) -> PageQuerySet:
|
def get_listing_pages(self) -> models.QuerySet:
|
||||||
return BlogPostPage.objects.descendant_of(self).live()
|
return (
|
||||||
|
BlogPostPage.objects.descendant_of(self)
|
||||||
@cached_property
|
.live()
|
||||||
def paginator_page(self) -> PaginatorPage:
|
|
||||||
pages = (
|
|
||||||
self.get_blog_posts()
|
|
||||||
.select_related("hero_image")
|
.select_related("hero_image")
|
||||||
.select_related("hero_unsplash_photo")
|
.select_related("hero_unsplash_photo")
|
||||||
.prefetch_related("tags")
|
.prefetch_related("tags")
|
||||||
.order_by("-date", "title")
|
.order_by("-date", "title")
|
||||||
)
|
)
|
||||||
paginator = Paginator(pages, per_page=1)
|
|
||||||
try:
|
|
||||||
return paginator.page(self.serializer.validated_data["page"])
|
|
||||||
except EmptyPage:
|
|
||||||
raise Http404
|
|
||||||
|
|
||||||
def get_context(self, request: HttpRequest) -> dict:
|
@property
|
||||||
context = super().get_context(request)
|
def feed_class(self) -> Type[ContentPageFeed]:
|
||||||
context["pages"] = self.paginator_page
|
|
||||||
return context
|
|
||||||
|
|
||||||
@route(r"^$")
|
|
||||||
def index_route(self, request: HttpRequest) -> HttpResponse:
|
|
||||||
self.serializer = PaginationSerializer(data=request.GET)
|
|
||||||
if not self.serializer.is_valid():
|
|
||||||
return HttpResponseBadRequest()
|
|
||||||
return super().index_route(request)
|
|
||||||
|
|
||||||
@route(r"^feed/$")
|
|
||||||
def feed(self, request: HttpRequest) -> HttpResponse:
|
|
||||||
from .views import BlogPostPageFeed
|
from .views import BlogPostPageFeed
|
||||||
|
|
||||||
return BlogPostPageFeed(
|
return BlogPostPageFeed
|
||||||
self.get_blog_posts().order_by("-date"), self.get_url(), self.title
|
|
||||||
)(request)
|
|
||||||
|
|
||||||
|
|
||||||
class BlogPostPage(BaseContentPage):
|
class BlogPostPage(BaseContentPage):
|
||||||
|
@ -97,66 +66,34 @@ class BlogPostPage(BaseContentPage):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class BlogPostTagListPage(BaseContentPage):
|
class BlogPostTagListPage(BaseListingPage):
|
||||||
max_count = 1
|
max_count = 1
|
||||||
parent_page_types = [BlogPostListPage]
|
parent_page_types = [BlogPostListPage]
|
||||||
subpage_types = ["blog.BlogPostTagPage"]
|
subpage_types = ["blog.BlogPostTagPage"]
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def table_of_contents(self) -> list[TocEntry]:
|
def table_of_contents(self) -> list[TocEntry]:
|
||||||
return [TocEntry(page.title, page.slug, 0, []) for page in self.get_tags()]
|
return [
|
||||||
|
TocEntry(page.title, page.slug, 0, []) for page in self.get_listing_pages()
|
||||||
def get_tags(self) -> PageQuerySet:
|
]
|
||||||
return (
|
|
||||||
self.get_children()
|
|
||||||
.specific()
|
|
||||||
.live()
|
|
||||||
.order_by("title")
|
|
||||||
.select_related("hero_image")
|
|
||||||
.select_related("hero_unsplash_photo")
|
|
||||||
)
|
|
||||||
|
|
||||||
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(RoutablePageMixin, BaseContentPage):
|
class BlogPostTagPage(BaseListingPage):
|
||||||
subpage_types: list[Any] = []
|
subpage_types: list[Any] = []
|
||||||
parent_page_types = [BlogPostTagListPage]
|
parent_page_types = [BlogPostTagListPage]
|
||||||
|
|
||||||
@cached_property
|
def get_listing_pages(self) -> models.QuerySet:
|
||||||
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 = BlogPostListPage.objects.all().live().get()
|
blog_list_page = BlogPostListPage.objects.all().live().get()
|
||||||
return (
|
return blog_list_page.get_listing_pages().filter(tags=self)
|
||||||
blog_list_page.get_blog_posts()
|
|
||||||
.filter(tags=self)
|
|
||||||
.order_by("-date")
|
|
||||||
.select_related("hero_image")
|
|
||||||
.select_related("hero_unsplash_photo")
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_context(self, request: HttpRequest) -> dict:
|
@property
|
||||||
context = super().get_context(request)
|
def feed_class(self) -> Type[ContentPageFeed]:
|
||||||
context["pages"] = self.get_blog_posts()
|
|
||||||
return context
|
|
||||||
|
|
||||||
@route(r"^feed/$")
|
|
||||||
def feed(self, request: HttpRequest) -> HttpResponse:
|
|
||||||
from .views import BlogPostPageFeed
|
from .views import BlogPostPageFeed
|
||||||
|
|
||||||
return BlogPostPageFeed(
|
return BlogPostPageFeed
|
||||||
self.get_blog_posts().order_by("-date"), self.get_url(), self.title
|
|
||||||
)(request)
|
|
||||||
|
|
||||||
|
|
||||||
class BlogPostCollectionListPage(BaseContentPage):
|
class BlogPostCollectionListPage(BaseListingPage):
|
||||||
subpage_types: list[Any] = []
|
subpage_types: list[Any] = []
|
||||||
parent_page_types = [BlogPostListPage]
|
parent_page_types = [BlogPostListPage]
|
||||||
max_count = 1
|
max_count = 1
|
||||||
|
@ -164,38 +101,23 @@ class BlogPostCollectionListPage(BaseContentPage):
|
||||||
@cached_property
|
@cached_property
|
||||||
def table_of_contents(self) -> list[TocEntry]:
|
def table_of_contents(self) -> list[TocEntry]:
|
||||||
return [
|
return [
|
||||||
TocEntry(page.title, page.slug, 0, []) for page in self.get_collections()
|
TocEntry(page.title, page.slug, 0, []) for page in self.get_listing_pages()
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_collections(self) -> PageQuerySet:
|
def get_listing_pages(self) -> models.QuerySet:
|
||||||
blog_list_page = BlogPostListPage.objects.all().live().get()
|
blog_list_page = BlogPostListPage.objects.all().live().get()
|
||||||
return BlogPostCollectionPage.objects.child_of(blog_list_page).live()
|
return BlogPostCollectionPage.objects.child_of(blog_list_page).live()
|
||||||
|
|
||||||
def get_context(self, request: HttpRequest) -> dict:
|
|
||||||
context = super().get_context(request)
|
|
||||||
context["collections"] = self.get_collections()
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
class BlogPostCollectionPage(BaseListingPage):
|
||||||
class BlogPostCollectionPage(BaseContentPage):
|
|
||||||
parent_page_types = [BlogPostListPage]
|
parent_page_types = [BlogPostListPage]
|
||||||
subpage_types = [BlogPostPage]
|
subpage_types = [BlogPostPage]
|
||||||
|
|
||||||
@cached_property
|
def get_listing_pages(self) -> models.QuerySet:
|
||||||
def table_of_contents(self) -> list[TocEntry]:
|
return super().get_listing_pages().order_by("-date")
|
||||||
return [
|
|
||||||
TocEntry(page.title, page.slug, 0, []) for page in self.get_blog_posts()
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_blog_posts(self) -> PageQuerySet:
|
@property
|
||||||
return (
|
def feed_class(self) -> Type[ContentPageFeed]:
|
||||||
BlogPostPage.objects.child_of(self)
|
from .views import BlogPostPageFeed
|
||||||
.order_by("-date")
|
|
||||||
.select_related("hero_image")
|
|
||||||
.select_related("hero_unsplash_photo")
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_context(self, request: HttpRequest) -> dict:
|
return BlogPostPageFeed
|
||||||
context = super().get_context(request)
|
|
||||||
context["pages"] = self.get_blog_posts()
|
|
||||||
return context
|
|
||||||
|
|
|
@ -1,9 +1 @@
|
||||||
{% extends "common/content_page.html" %}
|
{% extends "common/listing_page.html" %}
|
||||||
|
|
||||||
{% block post_content %}
|
|
||||||
<section class="container">
|
|
||||||
{% for collection in collections %}
|
|
||||||
{% include "common/listing-item.html" with page=collection %}
|
|
||||||
{% endfor %}
|
|
||||||
</section>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -1,9 +1 @@
|
||||||
{% extends "common/content_page.html" %}
|
{% extends "common/listing_page.html" %}
|
||||||
|
|
||||||
{% block post_content %}
|
|
||||||
<section class="container">
|
|
||||||
{% for page in pages %}
|
|
||||||
{% include "common/listing-item.html" %}
|
|
||||||
{% endfor %}
|
|
||||||
</section>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
{% block post_content %}
|
{% block post_content %}
|
||||||
<section class="container">
|
<section class="container">
|
||||||
{% for page in pages %}
|
{% for page in listing_pages %}
|
||||||
{% ifchanged %}
|
{% ifchanged %}
|
||||||
<time datetime="{{ page.date|date:'Y-m' }}" title='{{ page.date|date:"F Y" }}'>
|
<time datetime="{{ page.date|date:'Y-m' }}" title='{{ page.date|date:"F Y" }}'>
|
||||||
<h3 id="{{ page.date|date:'Y-m' }}" class="date-header">{{ page.date|date:"Y-m" }}</h3>
|
<h3 id="{{ page.date|date:'Y-m' }}" class="date-header">{{ page.date|date:"Y-m" }}</h3>
|
||||||
|
@ -24,10 +24,10 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{% if pages.has_other_pages %}
|
{% if listing_pages.has_other_pages %}
|
||||||
<section class="container">
|
<section class="container">
|
||||||
<hr class="my-5" />
|
<hr class="my-5" />
|
||||||
{% include "common/pagination.html" %}
|
{% include "common/pagination.html" with pages=listing_pages %}
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,9 +1 @@
|
||||||
{% extends "common/content_page.html" %}
|
{% extends "common/listing_page.html" %}
|
||||||
|
|
||||||
{% block post_content %}
|
|
||||||
<section class="container">
|
|
||||||
{% for tag in tags %}
|
|
||||||
{% include "common/listing-item.html" with page=tag %}
|
|
||||||
{% endfor %}
|
|
||||||
</section>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -1,15 +1 @@
|
||||||
{% extends "common/content_page.html" %}
|
{% extends "common/listing_page.html" %}
|
||||||
|
|
||||||
{% load wagtailroutablepage_tags %}
|
|
||||||
|
|
||||||
{% block extra_css %}
|
|
||||||
<link rel="alternate" type="application/rss+xml" href="{% routablepageurl page 'feed' %}">
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block post_content %}
|
|
||||||
<section class="container">
|
|
||||||
{% for page in pages %}
|
|
||||||
{% include "common/listing-item.html" %}
|
|
||||||
{% endfor %}
|
|
||||||
</section>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -1,40 +1,10 @@
|
||||||
from datetime import datetime, time
|
from datetime import datetime, time
|
||||||
|
|
||||||
from django.contrib.syndication.views import Feed
|
from website.common.views import ContentPageFeed
|
||||||
from django.http.request import HttpRequest
|
|
||||||
from django.http.response import HttpResponse
|
|
||||||
from wagtail.query import PageQuerySet
|
|
||||||
|
|
||||||
from .models import BlogPostPage
|
from .models import BlogPostPage
|
||||||
|
|
||||||
|
|
||||||
class BlogPostPageFeed(Feed):
|
class BlogPostPageFeed(ContentPageFeed):
|
||||||
def __init__(self, posts: PageQuerySet, link: str, title: str):
|
|
||||||
self.posts = posts
|
|
||||||
self.link = link
|
|
||||||
self.title = title
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
def __call__(
|
|
||||||
self, request: HttpRequest, *args: list, **kwargs: dict
|
|
||||||
) -> HttpResponse:
|
|
||||||
self.request = request
|
|
||||||
return super().__call__(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def items(self) -> PageQuerySet:
|
|
||||||
return self.posts
|
|
||||||
|
|
||||||
def item_title(self, item: BlogPostPage) -> str:
|
|
||||||
return item.title
|
|
||||||
|
|
||||||
def item_link(self, item: BlogPostPage) -> str:
|
|
||||||
return item.get_full_url(request=self.request)
|
|
||||||
|
|
||||||
def item_description(self, item: BlogPostPage) -> str:
|
|
||||||
return item.summary
|
|
||||||
|
|
||||||
def item_pubdate(self, item: BlogPostPage) -> datetime:
|
def item_pubdate(self, item: BlogPostPage) -> datetime:
|
||||||
return datetime.combine(item.date, time())
|
return datetime.combine(item.date, time())
|
||||||
|
|
||||||
def item_updateddate(self, item: BlogPostPage) -> datetime:
|
|
||||||
return item.last_published_at
|
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional, Type
|
||||||
|
|
||||||
|
from django.contrib.syndication.views import Feed
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
from django.core.paginator import EmptyPage
|
||||||
|
from django.core.paginator import Page as PaginatorPage
|
||||||
|
from django.core.paginator import Paginator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.http.request import HttpRequest
|
from django.http.request import HttpRequest
|
||||||
|
from django.http.response import Http404, HttpResponse, HttpResponseBadRequest
|
||||||
from django.utils.functional import cached_property, classproperty
|
from django.utils.functional import cached_property, classproperty
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from wagtail.admin.panels import FieldPanel
|
from wagtail.admin.panels import FieldPanel
|
||||||
|
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
|
||||||
from wagtail.fields import StreamField
|
from wagtail.fields import StreamField
|
||||||
from wagtail.images import get_image_model_string
|
from wagtail.images import get_image_model_string
|
||||||
from wagtail.images.views.serve import generate_image_url
|
from wagtail.images.views.serve import generate_image_url
|
||||||
|
@ -19,6 +25,7 @@ from wagtailmetadata.models import MetadataMixin
|
||||||
from website.common.utils import count_words
|
from website.common.utils import count_words
|
||||||
from website.contrib.unsplash.widgets import UnsplashPhotoChooser
|
from website.contrib.unsplash.widgets import UnsplashPhotoChooser
|
||||||
|
|
||||||
|
from .serializers import PaginationSerializer
|
||||||
from .streamfield import add_heading_anchors, get_blocks, get_content_html
|
from .streamfield import add_heading_anchors, get_blocks, get_content_html
|
||||||
from .utils import TocEntry, extract_text, get_table_of_contents, truncate_string
|
from .utils import TocEntry, extract_text, get_table_of_contents, truncate_string
|
||||||
|
|
||||||
|
@ -161,18 +168,67 @@ class ContentPage(BaseContentPage):
|
||||||
subpage_types: list[Any] = []
|
subpage_types: list[Any] = []
|
||||||
|
|
||||||
|
|
||||||
class ListingPage(BaseContentPage):
|
class BaseListingPage(RoutablePageMixin, BaseContentPage):
|
||||||
def get_context(self, request: HttpRequest) -> dict:
|
PAGE_SIZE = 20
|
||||||
context = super().get_context(request)
|
|
||||||
context["child_pages"] = (
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
def get_listing_pages(self) -> models.QuerySet:
|
||||||
|
return (
|
||||||
self.get_children()
|
self.get_children()
|
||||||
.live()
|
.live()
|
||||||
.specific()
|
.specific()
|
||||||
.select_related("hero_image")
|
.select_related("hero_image")
|
||||||
.select_related("hero_unsplash_photo")
|
.select_related("hero_unsplash_photo")
|
||||||
|
.order_by("title")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_paginator_page(self) -> PaginatorPage:
|
||||||
|
paginator = Paginator(self.get_listing_pages(), per_page=self.PAGE_SIZE)
|
||||||
|
try:
|
||||||
|
return paginator.page(self.serializer.validated_data["page"])
|
||||||
|
except EmptyPage:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
def get_context(self, request: HttpRequest) -> dict:
|
||||||
|
context = super().get_context(request)
|
||||||
|
context["listing_pages"] = self.get_paginator_page()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def table_of_contents(self) -> list[TocEntry]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def show_reading_time(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def feed_class(self) -> Type[Feed]:
|
||||||
|
from .views import ContentPageFeed
|
||||||
|
|
||||||
|
return ContentPageFeed
|
||||||
|
|
||||||
|
@route(r"^$")
|
||||||
|
def index_route(self, request: HttpRequest) -> HttpResponse:
|
||||||
|
self.serializer = PaginationSerializer(data=request.GET)
|
||||||
|
if not self.serializer.is_valid():
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
return super().index_route(request)
|
||||||
|
|
||||||
|
@route(r"^feed/$")
|
||||||
|
def feed(self, request: HttpRequest) -> HttpResponse:
|
||||||
|
return self.feed_class(
|
||||||
|
self.get_listing_pages(),
|
||||||
|
self.get_full_url(request),
|
||||||
|
self.title,
|
||||||
|
)(request)
|
||||||
|
|
||||||
|
|
||||||
|
class ListingPage(BaseListingPage):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@register_snippet
|
@register_snippet
|
||||||
class ReferralLink(models.Model, index.Indexed):
|
class ReferralLink(models.Model, index.Indexed):
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
{% extends "common/content_page.html" %}
|
{% extends "common/content_page.html" %}
|
||||||
|
|
||||||
|
{% load wagtailroutablepage_tags %}
|
||||||
|
|
||||||
|
{% block extra_css %}
|
||||||
|
<link rel="alternate" type="application/rss+xml" href="{% routablepageurl page 'feed' %}">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block post_content %}
|
{% block post_content %}
|
||||||
<section class="container">
|
<section class="container">
|
||||||
{% for page in child_pages %}
|
{% for page in listing_pages %}
|
||||||
{% include "common/listing-item.html" %}
|
{% include "common/listing-item.html" %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -54,8 +54,13 @@ class ListingPageTestCase(TestCase):
|
||||||
def test_accessible(self) -> None:
|
def test_accessible(self) -> None:
|
||||||
response = self.client.get(self.page.url)
|
response = self.client.get(self.page.url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(len(response.context["child_pages"]), 2)
|
self.assertEqual(len(response.context["listing_pages"]), 2)
|
||||||
|
|
||||||
|
def test_feed_accessible(self) -> None:
|
||||||
|
response = self.client.get(self.page.url + self.page.reverse_subpage("feed"))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response["Content-Type"], "application/rss+xml; charset=utf-8")
|
||||||
|
|
||||||
def test_queries(self) -> None:
|
def test_queries(self) -> None:
|
||||||
with self.assertNumQueries(24):
|
with self.assertNumQueries(25):
|
||||||
self.client.get(self.page.url)
|
self.client.get(self.page.url)
|
||||||
|
|
|
@ -12,7 +12,7 @@ from wagtail.query import PageQuerySet
|
||||||
|
|
||||||
from website.home.models import HomePage
|
from website.home.models import HomePage
|
||||||
|
|
||||||
from .models import BasePage
|
from .models import BaseContentPage, BasePage
|
||||||
|
|
||||||
|
|
||||||
class Error404View(TemplateView):
|
class Error404View(TemplateView):
|
||||||
|
@ -71,3 +71,35 @@ class AllPagesFeed(Feed):
|
||||||
|
|
||||||
def item_updateddate(self, item: BasePage) -> datetime:
|
def item_updateddate(self, item: BasePage) -> datetime:
|
||||||
return item.last_published_at
|
return item.last_published_at
|
||||||
|
|
||||||
|
|
||||||
|
class ContentPageFeed(Feed):
|
||||||
|
def __init__(self, posts: PageQuerySet, link: str, title: str):
|
||||||
|
self.posts = posts
|
||||||
|
self.link = link
|
||||||
|
self.title = title
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def __call__(
|
||||||
|
self, request: HttpRequest, *args: list, **kwargs: dict
|
||||||
|
) -> HttpResponse:
|
||||||
|
self.request = request
|
||||||
|
return super().__call__(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def items(self) -> PageQuerySet:
|
||||||
|
return self.posts
|
||||||
|
|
||||||
|
def item_title(self, item: BaseContentPage) -> str:
|
||||||
|
return item.title
|
||||||
|
|
||||||
|
def item_link(self, item: BaseContentPage) -> str:
|
||||||
|
return item.get_full_url(request=self.request)
|
||||||
|
|
||||||
|
def item_description(self, item: BaseContentPage) -> str:
|
||||||
|
return item.summary
|
||||||
|
|
||||||
|
def item_pubdate(self, item: BaseContentPage) -> datetime:
|
||||||
|
return item.first_published_at
|
||||||
|
|
||||||
|
def item_updateddate(self, item: BaseContentPage) -> datetime:
|
||||||
|
return item.last_published_at
|
||||||
|
|
|
@ -5,7 +5,6 @@ from wagtail.images import get_image_model_string
|
||||||
from wagtail.images.models import Image
|
from wagtail.images.models import Image
|
||||||
from wagtailmetadata.models import WagtailImageMetadataMixin
|
from wagtailmetadata.models import WagtailImageMetadataMixin
|
||||||
|
|
||||||
from website.blog.models import BlogPostPage
|
|
||||||
from website.common.models import BasePage
|
from website.common.models import BasePage
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,6 +37,7 @@ class HomePage(BasePage, WagtailImageMetadataMixin):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def get_context(self, request: HttpRequest) -> dict:
|
def get_context(self, request: HttpRequest) -> dict:
|
||||||
|
from website.blog.models import BlogPostPage
|
||||||
from website.search.models import SearchPage
|
from website.search.models import SearchPage
|
||||||
|
|
||||||
context = super().get_context(request)
|
context = super().get_context(request)
|
||||||
|
|
Loading…
Reference in a new issue