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, _default_partial_spec
from .shell import ShellRenderer
from .shortcuts import render_nav
from .swaps import Swaps, _normalize_swaps
class _ShellViewProtocol(Protocol):
request: HttpRequest
def get_template_names(self) -> list[str]: ...
def get_extra_swaps(self) -> Swaps: ...
def get_partial(self) -> PartialSpec: ...
def get_title(self) -> str | None: ...
def get_shell_template_name(self) -> str: ...
[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 render function, typically created by
``make_shell_renderer``. When omitted, calls ``render_nav`` directly.
default_swaps: Default Swap(s) applied across all views using this mixin.
default_partial: Partial spec used unless overridden per view.
Defaults to the ``HTMX_NAV_DEFAULT_PARTIAL`` setting (``"#content"``).
Returns:
A mixin class providing ``render_to_response`` and swap customization hooks.
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]``.
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"
resolved_default_partial: PartialSpec = (
_default_partial_spec() if default_partial is _UNSET else default_partial # type: ignore[assignment]
)
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:
return resolved_default_partial
def get_shell_template_name(self: _ShellViewProtocol) -> str:
return self.get_template_names()[0]
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(swaps_kwarg, swaps)
return render_fn(
self.request,
self.get_shell_template_name(),
context,
**response_kwargs,
)
return ShellViewMixin