Source code for feincms3.root.middleware
"""
Page middleware (``feincms3.root.middleware``)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The guide recommends using a middleware for the feincms3 pages app. This module
offers helpers and utilities to reduce the amount of code you have to write.
The reason why this module is called ``root`` is that the page app's mountpoint
has to be the Python app's mountpoint when using this. If that's not the case
you may want to write your own :ref:`urls-and-views`.
Example code for using this module (e.g. ``app.pages.middleware``):
.. code-block:: python
from django.shortcuts import render
from feincms3.root.middleware import add_redirect_handler, create_page_if_404_middleware
from app.pages.models import Page
from app.pages.utils import page_context
# The page handler receives the request and the page.
# ``add_redirect_handler`` wraps the handler function with support for the
# RedirectMixin.
@add_redirect_handler
def handler(request, page):
return render(request, page.type.template_name, page_context(request, page=page))
# This is the middleware which you want to add to ``MIDDLEWARE`` as
# ``app.pages.middleware.page_if_404_middleware``. The middleware should be
# added in the last position except if you have a very good reason not to
# do this.
page_if_404_middleware = create_page_if_404_middleware(
# queryset=Page.objects.active() works too (if .active() doesn't use
# get_language or anything similar)
queryset=lambda request: Page.objects.active(),
handler=handler,
)
"""
from functools import wraps
from django.conf import settings
from django.http import (
HttpResponseNotFound,
HttpResponsePermanentRedirect,
HttpResponseRedirect,
)
class _UseRootMiddlewareResponse(HttpResponseNotFound):
"""Used by feincms3.root.passthru to tell the middleware to do its thing"""
[docs]
def create_page_if_404_middleware(*, queryset, handler, language_code_redirect=False):
"""
Create a middleware for handling pages
This utility is there for your convenience, you do not have to use it. The
returned middleware already handles returning non-404 responses as-is,
fetching a page instance from the database and calling a user-defined
handler on success. It optionally also supports redirecting requests to the
root of the app to a language-specific landing page.
Required arguments:
- ``queryset``: A page queryset or a callable accepting the request and
returning a page queryset.
- ``handler``: A callable accepting the request and a page and returning a
response.
Optional arguments:
- ``language_code_redirect`` (``False``): Redirect visitor to the language
code prefix (e.g. ``/en/``, ``/de-ch/``) if request path equals the
script prefix (generally ``/``) and no active page for ``/`` exists.
"""
def outer(get_response):
def inner(request):
response = get_response(request)
if response.status_code != 404 or (
request.resolver_match
and not isinstance(response, _UseRootMiddlewareResponse)
):
# Response is not a 404 OR the 404 comes from a resolved view
# which also didn't return a _UseRootMiddlewareResponse.
return response
qs = queryset(request) if callable(queryset) else queryset._clone()
if page := qs.filter(path=request.path_info).first():
return handler(request, page)
if language_code_redirect and request.path_info == "/":
target = f"/{request.LANGUAGE_CODE}/"
if qs.filter(path=target).exists():
return HttpResponseRedirect(target)
if settings.APPEND_SLASH and not request.path_info.endswith("/"):
target = request.path_info + "/"
if qs.filter(path=target).exists():
return HttpResponsePermanentRedirect(target)
return response
return inner
return outer
[docs]
def add_redirect_handler(handler):
"""
Wrap the page handler in a redirect mixin handler
"""
@wraps(handler)
def inner(request, page):
if redirect_to := page.get_redirect_url():
return HttpResponseRedirect(redirect_to)
return handler(request, page)
return inner