Views¶
The django_htmx_plus.views module provides enhanced list views with HTMX support.
HtmxListView¶
from django_htmx_plus.views import HtmxListView
from myapp.models import Article
class ArticleListView(HtmxListView):
model = Article
template_name = "articles/list.html"
paginate_by = 10
A drop-in replacement for Django’s ListView with built-in URL-based filtering, column sorting, and elided pagination.
Attributes¶
- fields: Tuple[str, ...]¶
The fields that are allowed for filtering and sorting. There is no wildcard/bypass value — every field a view needs to expose must be listed explicitly.
"pk"is always added automatically if it isn’t already present, so rows remain uniquely identifiable even if you don’t list it explicitly. The<c-tables.htmx_table />component hides thepkcolumn by default — passshow_pk=Truein the context to render it.Default:
()
- labels: Dict[str, str] | None¶
Custom display labels for fields in table headers and filter UI.
Default:
None
Field names (must also appear in
fields) to keep in the queryset projection and context, but markvisible: Falseon so<c-tables.htmx_table />skips rendering their column. Useful for fields a view needs forget_transform_data()but shouldn’t display.Default:
()
- hx_target_id: str | None¶
The HTMX target element ID for list updates. Used by Cotton components.
Default:
None
- paginate_by: int¶
Number of items per page for pagination. Can be overridden per-request via the
?paginate_by=query parameter (see below) — when set on the view, the<c-tables.htmx_table />component renders a “Show N entries” selector that submits this parameter.Default:
25
- elided_each_side: int¶
Number of page numbers shown on each side of the current page in the elided page range (passed to
Paginator.get_elided_page_range). Only read whenpaginate_byis set.Default:
1
- elided_ends: int¶
Number of page numbers shown at each end of the elided page range (passed to
Paginator.get_elided_page_range). Only read whenpaginate_byis set.Default:
1
- enable_history: bool¶
Whether the
header_cell/pagerCotton components push sort, filter, and page changes onto the browser history viahx-push-url. When enabled, addhx-history-eltto the element wrapping<c-tables.htmx_table />so Back/Forward navigation restores the table correctly — see Working with List Views.Default:
False
Context Variables¶
The view provides the following context variables for templates:
- query_params: str¶
URL-encoded query string (without the
pageparameter).
- page_range: List[int | str]¶
Elided page range for pagination controls (shows first, last, and pages around current).
- order_by: str | None¶
The currently active ordering field (with
-prefix for descending).
- path: str¶
The current request path.
- hx_target_id: str | None¶
The HTMX target element ID (from view attribute).
- query: str¶
Full query string including
order_bybut withoutpage.
- filter_query: str¶
Query string containing only filter parameters (no
order_byorpage).
- filters: Dict[str, str]¶
Template-ready dict mapping plain field names to their filter values.
- fields: List[Dict[str, str | bool]]¶
A list of
{"name": str, "label": str, "visible": bool}dicts, one per field fromget_fields()(always including"pk").visibleisFalsefor any field listed inhidden_fields.
- enable_history: bool¶
Whether sort/filter/page links should push browser history entries (from the view’s
enable_historyattribute).
URL Query Parameters¶
Filtering
Filters use the format field_name.filter_type=value:
# Exact match
/articles/?status.eq=published
# Case-insensitive match
/articles/?title.ieq=Django
# Contains (case-sensitive)
/articles/?title.like=htmx
# Greater than or equal
/articles/?views.gte=100
# Multiple filters
/articles/?status.eq=published&views.gte=100
See Filtering and Sorting Guide for complete filter reference.
Sorting
# Ascending sort
/articles/?order_by=created_at
# Descending sort
/articles/?order_by=-created_at
Pagination
# Specific page
/articles/?page=2
# Override the page size (also settable from the table's "Show N entries" selector)
/articles/?paginate_by=50
Note
paginate_by from the query string is parsed with int() and applied
without clamping to a min/max or to the selector’s preset options
(10/25/50/100) — a view that exposes this to untrusted users
should validate the requested page size itself, e.g. in an overridden
get_context_data() or setup().
Example¶
from django_htmx_plus.views import HtmxListView
from myapp.models import Article
class ArticleListView(HtmxListView):
model = Article
template_name = "articles/list.html"
paginate_by = 10
hx_target_id = "#article-table"
# Restrict to these fields
fields = ("id", "title", "status", "created_at")
# Custom labels
labels = {
"created_at": "Date Created",
"status": "Publication Status",
}
Integration with Templates¶
Use the provided context variables in templates:
{% extends "base.html" %}
{% load cotton_extras %}
{% block content %}
<div id="article-table" hx-trigger="articleCreated from:body">
<c-tables.htmx_table class="table table-striped" />
</div>
{% endblock %}
The HtmxListView automatically handles:
Query parameter parsing and validation
ORM filter generation
Sorting
Pagination with elided page ranges
Transforming Rows¶
Override get_transform_data() to post-process each page’s rows before they reach the
template, without re-implementing filtering or pagination:
class ArticleListView(HtmxListView):
model = Article
fields = ("title", "status", "created_at")
def get_transform_data(self, objects):
rows = super().get_transform_data(objects)
for row in rows:
row["status"] = row["status"].title()
return rows
get_transform_data receives the current page’s queryset (or values queryset) and must
return the list of rows to render.
Custom / Dynamic Querysets¶
The default get_queryset() resolves the view’s base queryset (from self.queryset
or self.model), then applies URL-based filtering, order_by, and the fields
projection via queryset_values(). To supply a fully custom or dynamic queryset —
a different manager, select_related/prefetch_related, per-user logic — override
get_queryset() without calling super(), and pass your queryset through
queryset_values() to opt back into the view’s automatic filtering/sorting:
class ArticleListView(HtmxListView):
model = Article
fields = ("title", "status", "created_at")
def get_queryset(self):
queryset = Article.objects.select_related("author") if self.request.user.is_staff \
else Article.published.select_related("author")
return self.queryset_values(queryset)
- queryset_values(queryset)¶
Applies
self.filter(parsed from the URL’sfield.filter_type=valuequery parameters), orders the result byself.order_by, and restricts the projection toget_fields()(always including"pk"). Call this from a fully overriddenget_queryset()to get the same automatic filter/order_by/values behavior as the default implementation.If you skip
queryset_values()and return a queryset directly, remember that without the final.values(*fields)step, full model instances are returned instead of thefields-restricted dicts templates expect.