Source code for htmx_nav.views
from __future__ import annotations
from collections.abc import Callable
from typing import Any, Protocol
from django.http import HttpRequest
from django.template.response import TemplateResponse
from .partials import PartialSpec
from .settings import _UNSET
from .shell import ShellRenderer
from .shortcuts import render_nav
from .swaps import Swaps, _normalize_swaps
class _ShellViewProtocol(Protocol):
request: HttpRequest
template_engine: str | None
content_type: str | None
def get_template_names(self) -> list[str]: ...
def get_extra_swaps(self) -> Swaps: ...
def get_partial(self) -> PartialSpec: ...
def get_title(self) -> str | None: ...
[docs]
def make_shell_view_mixin(
render: ShellRenderer | None = None,
*,
default_swaps: Swaps = None,
default_partial: PartialSpec | object = _UNSET,
) -> type:
"""Create a class mixin that routes CBV rendering through a shell renderer.
Args:
render: Optional renderer from ``make_shell_renderer``. When omitted,
the mixin calls ``render_nav`` directly.
default_swaps: Swaps applied to every view using this mixin.
default_partial: Partial used unless a view overrides ``get_partial()``.
When omitted, the renderer's own default applies, or
``HTMX_NAV_DEFAULT_PARTIAL`` (``"#content"``) without a renderer.
Returns:
A mixin class. Place it before the Django view class in the bases so
its ``render_to_response`` wins.
Notes:
Override points on the resulting view class:
- ``get_extra_swaps()``: Returns per-view swaps (runs with ``self.object`` available).
- ``get_title()`` or ``title``: Page title override.
- ``get_partial()``: Overrides ``default_partial`` for the view.
- ``get_shell_template_name()``: Defaults to ``get_template_names()[0]``.
- ``get_template_names()``: Django's own hook. Only the first entry
is rendered, since partial resolution needs one concrete name.
Example:
.. code-block:: python
ShellViewMixin = make_shell_view_mixin()
class TicketListView(ShellViewMixin, ListView):
template_name = "pages/project.html"
def get_extra_swaps(self):
return [sidebar_swap, breadcrumb_swap]
"""
defaults = _normalize_swaps(default_swaps)
render_fn: Callable[..., TemplateResponse] = render or render_nav
swaps_kwarg = "extra_swaps" if render is not None else "swaps"
class ShellViewMixin:
title: str | None = None
def get_extra_swaps(self) -> Swaps:
return None
def get_title(self) -> str | None:
return self.title
def get_partial(self) -> PartialSpec | object:
return default_partial
def render_to_response(
self: _ShellViewProtocol,
context: dict[str, Any],
**response_kwargs: Any,
) -> TemplateResponse:
swaps = [*defaults, *_normalize_swaps(self.get_extra_swaps())]
response_kwargs.setdefault("partial", self.get_partial())
response_kwargs.setdefault("title", self.get_title())
response_kwargs.setdefault("using", self.template_engine)
response_kwargs.setdefault("content_type", self.content_type)
response_kwargs.setdefault(swaps_kwarg, swaps)
return render_fn(
self.request,
self.get_template_names()[0],
context,
**response_kwargs,
)
ShellViewMixin.__qualname__ = "ShellViewMixin"
return ShellViewMixin