From 658b74834df5683e114853e79eab7f0396f79a28 Mon Sep 17 00:00:00 2001 From: Dave Gaeddert Date: Sun, 21 Jan 2024 14:20:31 -0600 Subject: [PATCH] More --- bolt-auth/bolt/auth/forms.py | 2 +- bolt-htmx/bolt/htmx/views.py | 2 - bolt-staff/bolt/admin/cards/base.py | 5 +- bolt-staff/bolt/querystats/middleware.py | 12 -- .../toolbar/templates/toolbar/toolbar.html | 2 +- bolt/http/response.py | 114 ------------------ bolt/internal/handlers/base.py | 27 ----- bolt/runtime/global_settings.py | 2 +- bolt/templates/core.py | 1 - bolt/utils/decorators.py | 18 +-- bolt/views/base.py | 32 ----- bolt/views/forms.py | 2 +- bolt/views/objects.py | 4 +- bolt/views/templates.py | 27 ++++- 14 files changed, 34 insertions(+), 216 deletions(-) diff --git a/bolt-auth/bolt/auth/forms.py b/bolt-auth/bolt/auth/forms.py index 985652cc4a..38c7fb8560 100644 --- a/bolt-auth/bolt/auth/forms.py +++ b/bolt-auth/bolt/auth/forms.py @@ -1,13 +1,13 @@ import unicodedata from bolt import forms -from bolt.templates import Template from bolt.auth import authenticate, get_user_model, password_validation from bolt.auth.models import User from bolt.auth.tokens import default_token_generator from bolt.db.forms import ModelForm from bolt.exceptions import ValidationError from bolt.mail import EmailMultiAlternatives +from bolt.templates import Template from bolt.utils.encoding import force_bytes from bolt.utils.http import urlsafe_base64_encode diff --git a/bolt-htmx/bolt/htmx/views.py b/bolt-htmx/bolt/htmx/views.py index ae4acfc755..1aff880c33 100644 --- a/bolt-htmx/bolt/htmx/views.py +++ b/bolt-htmx/bolt/htmx/views.py @@ -1,7 +1,5 @@ import re -from bolt.http import HttpResponse - class HTMXViewMixin: htmx_template_name = "" diff --git a/bolt-staff/bolt/admin/cards/base.py b/bolt-staff/bolt/admin/cards/base.py index c6faca0a54..351e6ca2e2 100644 --- a/bolt-staff/bolt/admin/cards/base.py +++ b/bolt-staff/bolt/admin/cards/base.py @@ -1,7 +1,7 @@ from enum import Enum -from bolt import jinja from bolt.admin.dates import DatetimeRange, DatetimeRangeAliases +from bolt.templates import Template from bolt.utils.text import slugify @@ -39,8 +39,7 @@ def render(self, request, datetime_range): # If fixed, show that on the card too (I guess you could use description for this) else: self.datetime_range = datetime_range - template = jinja.environment.get_template(self.template_name) - return template.render(self.get_context()) + return Template(self.template_name).render(self.get_context()) @classmethod def view_name(cls) -> str: diff --git a/bolt-staff/bolt/querystats/middleware.py b/bolt-staff/bolt/querystats/middleware.py index 83b5ea17da..8597ab3e21 100644 --- a/bolt-staff/bolt/querystats/middleware.py +++ b/bolt-staff/bolt/querystats/middleware.py @@ -86,15 +86,3 @@ def is_staff_request(request): return request.impersonator and request.impersonator.is_staff return hasattr(request, "user") and request.user and request.user.is_staff - - def process_template_response(self, request, response): - # Template hasn't been rendered yet, so we can't include querystats themselves - # unless we're pulling the previous page stats from the session storage - if ( - response.context_data is not None - and hasattr(_local, "querystats") - and self.is_staff_request(request) - ): - response.context_data["querystats_enabled"] = True - - return response diff --git a/bolt-staff/bolt/toolbar/templates/toolbar/toolbar.html b/bolt-staff/bolt/toolbar/templates/toolbar/toolbar.html index c95decd166..6682f54f0f 100644 --- a/bolt-staff/bolt/toolbar/templates/toolbar/toolbar.html +++ b/bolt-staff/bolt/toolbar/templates/toolbar/toolbar.html @@ -22,7 +22,7 @@ {% endif %}
- {% if querystats_enabled|default(false) %}{% include "querystats/toolbar.html" %}{% endif %} + {% include "querystats/toolbar.html" %}
{% include "toolbar/links.html" %} diff --git a/bolt/http/response.py b/bolt/http/response.py index cb24d9ac67..2b63c90e0e 100644 --- a/bolt/http/response.py +++ b/bolt/http/response.py @@ -717,117 +717,3 @@ def __init__( kwargs.setdefault("content_type", "application/json") data = json.dumps(data, cls=encoder, **json_dumps_params) super().__init__(content=data, **kwargs) - - -class ContentNotRenderedError(Exception): - pass - - -class RenderableResponse(HttpResponse): - non_picklable_attrs = HttpResponse.non_picklable_attrs | frozenset( - ["render_func", "context_data", "_post_render_callbacks", "_request"] - ) - - def __init__( - self, - render_func, - context=None, - content_type=None, - status=None, - charset=None, - headers=None, - ): - self.render_func = render_func - - # It would seem obvious to call these next two members 'template' and - # 'context', but those names are reserved as part of the test Client - # API. To avoid the name collision, we use different names. - self.context_data = context - - self._post_render_callbacks = [] - - # content argument doesn't make sense here because it will be replaced - # with rendered template so we always pass empty string in order to - # prevent errors and provide shorter signature. - super().__init__("", content_type, status, charset=charset, headers=headers) - - # _is_rendered tracks whether the template and context has been baked - # into a final response. - # Super __init__ doesn't know any better than to set self.content to - # the empty string we just gave it, which wrongly sets _is_rendered - # True, so we initialize it to False after the call to super __init__. - self._is_rendered = False - - def __getstate__(self): - """ - Raise an exception if trying to pickle an unrendered response. Pickle - only rendered data, not the data used to construct the response. - """ - if not self._is_rendered: - raise ContentNotRenderedError( - "The response content must be rendered before it can be pickled." - ) - return super().__getstate__() - - @property - def rendered_content(self): - """Return the freshly rendered content for the template and context - described by the TemplateResponse. - - This *does not* set the final content of the response. To set the - response content, you must either call render(), or set the - content explicitly using the value of this property. - """ - return self.render_func(self.context_data) - - def add_post_render_callback(self, callback): - """Add a new post-rendering callback. - - If the response has already been rendered, - invoke the callback immediately. - """ - if self._is_rendered: - callback(self) - else: - self._post_render_callbacks.append(callback) - - def render(self): - """Render (thereby finalizing) the content of the response. - - If the content has already been rendered, this is a no-op. - - Return the baked response instance. - """ - retval = self - if not self._is_rendered: - self.content = self.rendered_content - for post_callback in self._post_render_callbacks: - newretval = post_callback(retval) - if newretval is not None: - retval = newretval - return retval - - @property - def is_rendered(self): - return self._is_rendered - - def __iter__(self): - if not self._is_rendered: - raise ContentNotRenderedError( - "The response content must be rendered before it can be iterated over." - ) - return super().__iter__() - - @property - def content(self): - if not self._is_rendered: - raise ContentNotRenderedError( - "The response content must be rendered before it can be accessed." - ) - return super().content - - @content.setter - def content(self, value): - """Set the content for the response.""" - HttpResponse.content.fset(self, value) - self._is_rendered = True diff --git a/bolt/internal/handlers/base.py b/bolt/internal/handlers/base.py index 06703585c9..0fa3fad91a 100644 --- a/bolt/internal/handlers/base.py +++ b/bolt/internal/handlers/base.py @@ -15,7 +15,6 @@ class BaseHandler: _view_middleware = None - _template_response_middleware = None _exception_middleware = None _middleware_chain = None @@ -26,7 +25,6 @@ def load_middleware(self): Must be called after the environment is fixed (see __call__ in subclasses). """ self._view_middleware = [] - self._template_response_middleware = [] self._exception_middleware = [] get_response = self._get_response @@ -61,10 +59,6 @@ def load_middleware(self): 0, self.adapt_method_mode(mw_instance.process_view), ) - if hasattr(mw_instance, "process_template_response"): - self._template_response_middleware.append( - self.adapt_method_mode(mw_instance.process_template_response), - ) if hasattr(mw_instance, "process_exception"): # The exception-handling stack is still always synchronous for # now, so adapt that way. @@ -145,27 +139,6 @@ def _get_response(self, request): # Complain if the view returned None (a common error). self.check_response(response, callback) - # If the response supports deferred rendering, apply template - # response middleware and then render the response - if hasattr(response, "render") and callable(response.render): - for middleware_method in self._template_response_middleware: - response = middleware_method(request, response) - # Complain if the template response middleware returned None - # (a common error). - self.check_response( - response, - middleware_method, - name="{}.process_template_response".format( - middleware_method.__self__.__class__.__name__ - ), - ) - try: - response = response.render() - except Exception as e: - response = self.process_exception_by_middleware(e, request) - if response is None: - raise - return response def resolve_request(self, request): diff --git a/bolt/runtime/global_settings.py b/bolt/runtime/global_settings.py index 929cfa045f..e481bcad29 100644 --- a/bolt/runtime/global_settings.py +++ b/bolt/runtime/global_settings.py @@ -196,4 +196,4 @@ ############# JINJA_LOADER = "jinja2.loaders.FileSystemLoader" -JINJA_ENVIRONMENT = "bolt.jinja.defaults.create_default_environment" +JINJA_ENVIRONMENT = "bolt.templates.jinja.defaults.create_default_environment" diff --git a/bolt/templates/core.py b/bolt/templates/core.py index cea8be1d97..6104361cb9 100644 --- a/bolt/templates/core.py +++ b/bolt/templates/core.py @@ -8,7 +8,6 @@ class TemplateFileMissing(Exception): class Template: - # TODO allow str template content? def __init__(self, filename: str) -> None: self.filename = filename diff --git a/bolt/utils/decorators.py b/bolt/utils/decorators.py index 25e4c80bb6..bf704a296b 100644 --- a/bolt/utils/decorators.py +++ b/bolt/utils/decorators.py @@ -122,22 +122,8 @@ def _wrapper_view(request, *args, **kwargs): if result is not None: return result raise - if hasattr(response, "render") and callable(response.render): - if hasattr(middleware, "process_template_response"): - response = middleware.process_template_response( - request, response - ) - # Defer running of process_response until after the template - # has been rendered: - if hasattr(middleware, "process_response"): - - def callback(response): - return middleware.process_response(request, response) - - response.add_post_render_callback(callback) - else: - if hasattr(middleware, "process_response"): - return middleware.process_response(request, response) + if hasattr(middleware, "process_response"): + return middleware.process_response(request, response) return response return _wrapper_view diff --git a/bolt/views/base.py b/bolt/views/base.py index 08132aab8e..fb6277c005 100644 --- a/bolt/views/base.py +++ b/bolt/views/base.py @@ -1,6 +1,5 @@ import logging -from bolt.csrf.middleware import get_token from bolt.http import ( HttpRequest, HttpResponse, @@ -8,26 +7,10 @@ HttpResponseNotAllowed, JsonResponse, ) -from bolt.http.response import RenderableResponse -from bolt.templates import Template from bolt.utils.decorators import classonlymethod -from bolt.utils.functional import lazy -from bolt.utils.html import format_html -from bolt.utils.safestring import SafeString from .exceptions import HttpResponseException - -def csrf_input(request): - return format_html( - '', - get_token(request), - ) - - -csrf_input_lazy = lazy(csrf_input, SafeString, str) -csrf_token_lazy = lazy(get_token, str) - logger = logging.getLogger("bolt.request") @@ -70,16 +53,6 @@ def view(request, *args, **kwargs): return view - def get_base_context(self) -> dict: - return { - "request": self.request, - "csrf_input": csrf_input_lazy(self.request), - "csrf_token": csrf_token_lazy(self.request), - } - - def get_context(self) -> dict: - return {} - def get_request_handler(self) -> callable: """Return the handler for the current request method.""" @@ -118,11 +91,6 @@ def get_response(self) -> HttpResponseBase: if isinstance(result, dict): return JsonResponse(result) - # TODO or see if has a render func? - if isinstance(result, Template): - context = {**self.get_base_context(), **self.get_context()} - return RenderableResponse(result.render, context) - raise ValueError(f"Unexpected view return type: {type(result)}") def _http_method_not_allowed(self) -> HttpResponse: diff --git a/bolt/views/forms.py b/bolt/views/forms.py index 099dd3f36c..afc46efd7f 100644 --- a/bolt/views/forms.py +++ b/bolt/views/forms.py @@ -56,7 +56,7 @@ def form_invalid(self, form: "BaseForm") -> HttpResponse: **self.get_context(), "form": form, } - return self.get_template_response(context) + return self.get_template().render(context) def get_context(self) -> dict: """Insert the form into the context dict.""" diff --git a/bolt/views/objects.py b/bolt/views/objects.py index 5138478bb6..87be56d48a 100644 --- a/bolt/views/objects.py +++ b/bolt/views/objects.py @@ -10,7 +10,7 @@ class ObjectTemplateViewMixin: def get(self) -> HttpResponse: self.load_object() - return self.get_template() + return self.render_template() def load_object(self) -> None: try: @@ -191,7 +191,7 @@ class ListView(TemplateView): def get(self) -> HttpResponse: self.objects = self.get_queryset() - return self.get_template_response() + return super().get() def get_queryset(self): # Intentionally untyped... subclasses must override this. raise NotImplementedError( diff --git a/bolt/views/templates.py b/bolt/views/templates.py index d76df57364..53151aec14 100644 --- a/bolt/views/templates.py +++ b/bolt/views/templates.py @@ -1,11 +1,22 @@ +from bolt.csrf.middleware import get_token from bolt.exceptions import ImproperlyConfigured from bolt.templates import Template, TemplateFileMissing +from bolt.utils.functional import lazy +from bolt.utils.html import format_html +from bolt.utils.safestring import SafeString from .base import View -class TemplateDoesNotExist(Exception): - pass +def csrf_input(request): + return format_html( + '', + get_token(request), + ) + + +csrf_input_lazy = lazy(csrf_input, SafeString, str) +csrf_token_lazy = lazy(get_token, str) class TemplateView(View): @@ -15,6 +26,13 @@ class TemplateView(View): template_name: str | None = None + def get_context(self) -> dict: + return { + "request": self.request, + "csrf_input": csrf_input_lazy(self.request), + "csrf_token": csrf_token_lazy(self.request), + } + def get_template_names(self) -> list[str]: """ Return a list of template names to be used for the request. Must return @@ -35,5 +53,8 @@ def get_template(self) -> Template: except TemplateFileMissing: pass + def render_template(self) -> str: + return self.get_template().render(self.get_context()) + def get(self): - return self.get_template() + return self.render_template()