From e0673548c58ed2a4493cb286d76ea9a55a151ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20Wersd=C3=B6rfer?= Date: Thu, 7 Sep 2023 10:17:26 +0200 Subject: [PATCH] #105 add proper context manager for passing request to wagtail pages + test --- cast/api/views.py | 30 ++++++++++++++++++++++++++++-- tests/api_test.py | 22 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/cast/api/views.py b/cast/api/views.py index 8158a675..0c3c6360 100644 --- a/cast/api/views.py +++ b/cast/api/views.py @@ -1,10 +1,12 @@ import logging from collections import OrderedDict +from collections.abc import Iterator +from contextlib import contextmanager from typing import Any, cast from django.contrib.auth.mixins import LoginRequiredMixin from django.db.models import QuerySet -from django.http import JsonResponse +from django.http import HttpRequest, JsonResponse from django.urls import reverse from django.views.generic import CreateView from rest_framework import generics, status @@ -203,11 +205,35 @@ def get_filtered_queryset(self) -> QuerySet: return filterset.qs def get_queryset(self): - ProxyRequest.request = self.request if self.request.GET.dict().get("use_post_filter", "false") == "true": return self.get_filtered_queryset() return super().get_queryset() + def dispatch(self, request, *args, **kwargs): + """ + Override dispatch to set request on ProxyRequest to be able to use + the real request in page models. This is needed for example to select + the correct template base directory (theme) from `request.session` which + would otherwise not be available. + + Putting this into get_queryset() would not work because `ProxyRequest.request` + would be None again before it gets used in the `page.get_description` method. + """ + + @contextmanager + def tunneled_request(_request: HttpRequest) -> Iterator: + """ + Set request on ProxyRequest for use in page models temporarily. + """ + ProxyRequest.request = _request + try: + yield + finally: + ProxyRequest.request = None + + with tunneled_request(request): + return super().dispatch(request, *args, **kwargs) + # Wagtail API wagtail_api_router = WagtailAPIRouter("cast:api:wagtail") diff --git a/tests/api_test.py b/tests/api_test.py index d419172d..5a46bb57 100644 --- a/tests/api_test.py +++ b/tests/api_test.py @@ -320,3 +320,25 @@ def test_update_theme_invalid(api_client): print(result) assert result["error"] == "Theme slug is invalid" assert api_client.session.get("template_base_dir") is None + + +@pytest.mark.django_db +def test_render_html_with_theme_from_session(api_client, post): + # Given we have custom theme set in the session + r = api_client.post( + # FIXME there's some way to update the session more elegantly + # use this instead of the post request + reverse("cast:api:theme-update"), + {"theme_slug": "plain"}, + format="json", + ) + assert r.status_code == 200 + + # When we request the blog post via api + url = reverse("cast:api:wagtail:pages:detail", kwargs={"pk": post.pk}) + r = api_client.get(url, format="json") + assert r.status_code == 200 + + # Then we expect the blog post to be rendered with the theme from the session + assert r.context.get("template_base_dir") == "plain" + assert all([t.name.startswith("cast/plain/") for t in r.templates])