diff --git a/website/contrib/singleton_url/__init__.py b/website/contrib/singleton_url/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/website/contrib/singleton_url/tests.py b/website/contrib/singleton_url/tests.py new file mode 100644 index 0000000..1c9e5ee --- /dev/null +++ b/website/contrib/singleton_url/tests.py @@ -0,0 +1,16 @@ +from django.test import TestCase + +from website.common.models import ContentPage +from website.home.models import HomePage + +from .utils import SingletonURLCache + + +class SingletonURLTestCase(TestCase): + def test_gets_url(self) -> None: + with self.assertNumQueries(2): + self.assertEqual(SingletonURLCache.get_url(HomePage), "http://localhost/") + + def test_missing_page(self) -> None: + with self.assertNumQueries(1): + self.assertIsNone(SingletonURLCache.get_url(ContentPage)) diff --git a/website/contrib/singleton_url/utils.py b/website/contrib/singleton_url/utils.py new file mode 100644 index 0000000..64fd034 --- /dev/null +++ b/website/contrib/singleton_url/utils.py @@ -0,0 +1,32 @@ +from typing import Type + +from django.core.cache import cache +from django.http.request import HttpRequest +from wagtail.models import Page + + +class SingletonURLCache: + @classmethod + def get_url_cache_key(cls, model: Type[Page]) -> str: + return f"singleton_url_{model.__name__}" + + @classmethod + def get_url( + cls, model: Type[Page], request: HttpRequest | None = None + ) -> str | None: + cache_key = cls.get_url_cache_key(model) + + url = cache.get(cache_key) + + if url is None: + # `.first` is marginally more efficient than `.get` + page = Page.objects.type(model).first() + + if page is None: + return None + + url = page.get_full_url(request) + + cache.set(cache_key, url, 86400) + + return url diff --git a/website/contrib/singleton_url/wagtail_hooks.py b/website/contrib/singleton_url/wagtail_hooks.py new file mode 100644 index 0000000..3906315 --- /dev/null +++ b/website/contrib/singleton_url/wagtail_hooks.py @@ -0,0 +1,16 @@ +from django.core.cache import cache +from wagtail import hooks + +from website.common.utils import get_page_models + +from .utils import SingletonURLCache + + +@hooks.register("after_move_page") +def clear_singleton_url_cache(**kwargs: dict) -> None: + """ + Clear all page caches, in case a parent has moved + """ + cache.delete_many( + [SingletonURLCache.get_url_cache_key(model) for model in get_page_models()] + ) diff --git a/website/settings.py b/website/settings.py index 3fdd7c9..b4a0973 100644 --- a/website/settings.py +++ b/website/settings.py @@ -41,6 +41,7 @@ INSTALLED_APPS = [ "website.contrib.code_block", "website.contrib.mermaid_block", "website.contrib.unsplash", + "website.contrib.singleton_url", "wagtail.contrib.forms", "wagtail.contrib.redirects", "wagtail.contrib.modeladmin",