django-htmx-views 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_htmx_views-0.1.0/LICENSE +21 -0
- django_htmx_views-0.1.0/MANIFEST.in +4 -0
- django_htmx_views-0.1.0/PKG-INFO +235 -0
- django_htmx_views-0.1.0/README.md +190 -0
- django_htmx_views-0.1.0/django_htmx_views.egg-info/PKG-INFO +235 -0
- django_htmx_views-0.1.0/django_htmx_views.egg-info/SOURCES.txt +22 -0
- django_htmx_views-0.1.0/django_htmx_views.egg-info/dependency_links.txt +1 -0
- django_htmx_views-0.1.0/django_htmx_views.egg-info/requires.txt +20 -0
- django_htmx_views-0.1.0/django_htmx_views.egg-info/top_level.txt +1 -0
- django_htmx_views-0.1.0/htmx_views/__init__.py +3 -0
- django_htmx_views-0.1.0/htmx_views/_optional.py +19 -0
- django_htmx_views-0.1.0/htmx_views/apps.py +20 -0
- django_htmx_views-0.1.0/htmx_views/linked_selects.py +50 -0
- django_htmx_views-0.1.0/htmx_views/models.py +6 -0
- django_htmx_views-0.1.0/htmx_views/templates/htmx_views/widgets/options.html +4 -0
- django_htmx_views-0.1.0/htmx_views/templates/htmx_views/widgets/select.html +3 -0
- django_htmx_views-0.1.0/htmx_views/templatetags/__init__.py +1 -0
- django_htmx_views-0.1.0/htmx_views/urls.py +13 -0
- django_htmx_views-0.1.0/htmx_views/views.py +335 -0
- django_htmx_views-0.1.0/htmx_views/widgets.py +68 -0
- django_htmx_views-0.1.0/pyproject.toml +75 -0
- django_htmx_views-0.1.0/setup.cfg +4 -0
- django_htmx_views-0.1.0/tests/test_htmx_views.py +1366 -0
- django_htmx_views-0.1.0/tests/test_version.py +29 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gavin Burnell
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-htmx-views
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reusable Django view services for HTMX-enabled applications
|
|
5
|
+
Author: Gavin Burnell
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/gb119/django-htmx-views
|
|
8
|
+
Project-URL: Repository, https://github.com/gb119/django-htmx-views
|
|
9
|
+
Project-URL: Issues, https://github.com/gb119/django-htmx-views/issues
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Framework :: Django
|
|
12
|
+
Classifier: Framework :: Django :: 4.2
|
|
13
|
+
Classifier: Framework :: Django :: 5.0
|
|
14
|
+
Classifier: Framework :: Django :: 5.1
|
|
15
|
+
Classifier: Framework :: Django :: 5.2
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: Django>=4.2
|
|
30
|
+
Requires-Dist: django-htmx>=1.17
|
|
31
|
+
Provides-Extra: docs
|
|
32
|
+
Requires-Dist: Sphinx<9,>=7; extra == "docs"
|
|
33
|
+
Requires-Dist: django-ajax-selects>=2.2; extra == "docs"
|
|
34
|
+
Requires-Dist: sphinx-better-theme>=0.1.5; extra == "docs"
|
|
35
|
+
Provides-Extra: linked-selects
|
|
36
|
+
Requires-Dist: django-ajax-selects>=2.2; extra == "linked-selects"
|
|
37
|
+
Provides-Extra: test
|
|
38
|
+
Requires-Dist: build>=1.2; extra == "test"
|
|
39
|
+
Requires-Dist: django-ajax-selects>=2.2; extra == "test"
|
|
40
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
41
|
+
Requires-Dist: pytest-django>=4.8; extra == "test"
|
|
42
|
+
Requires-Dist: tomli>=2; python_version < "3.11" and extra == "test"
|
|
43
|
+
Requires-Dist: twine>=5; extra == "test"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# django-htmx-views
|
|
47
|
+
|
|
48
|
+
Reusable Django view mixins and widgets for building HTMX-enabled applications.
|
|
49
|
+
This package extracts the `htmx_views` app used by
|
|
50
|
+
[`gb119/labman`](https://github.com/gb119/labman) and
|
|
51
|
+
[`uolphysicsteaching/phas_vitals`](https://github.com/uolphysicsteaching/phas_vitals)
|
|
52
|
+
so that the implementation can be installed and maintained independently.
|
|
53
|
+
|
|
54
|
+
The initial package source is an exact copy of the newer `phas_vitals`
|
|
55
|
+
implementation at commit
|
|
56
|
+
[`1008bc4`](https://github.com/uolphysicsteaching/phas_vitals/commit/1008bc475a28a0ccca40df8769ad6902b68edebc).
|
|
57
|
+
The two upstream apps were compared before extraction and are not currently
|
|
58
|
+
identical; see [UPSTREAM_COMPARISON.md](UPSTREAM_COMPARISON.md) for the
|
|
59
|
+
verification details.
|
|
60
|
+
|
|
61
|
+
## Features
|
|
62
|
+
|
|
63
|
+
- HTMX-aware dispatch for Django class-based views.
|
|
64
|
+
- Routing by HTMX trigger name, trigger ID, or target ID.
|
|
65
|
+
- HTMX-specific templates, context data, and context object names.
|
|
66
|
+
- HTMX-aware form-valid and form-invalid handlers.
|
|
67
|
+
- Optional linked `<select>` widgets backed by `django-ajax-selects`.
|
|
68
|
+
|
|
69
|
+
## Installation
|
|
70
|
+
|
|
71
|
+
Install the core package:
|
|
72
|
+
|
|
73
|
+
```console
|
|
74
|
+
python -m pip install django-htmx-views
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Install linked-select support as well:
|
|
78
|
+
|
|
79
|
+
```console
|
|
80
|
+
python -m pip install "django-htmx-views[linked-selects]"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Add the app and `django-htmx` middleware to the host project's settings:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
INSTALLED_APPS = [
|
|
87
|
+
# ...
|
|
88
|
+
"htmx_views",
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
MIDDLEWARE = [
|
|
92
|
+
# ...
|
|
93
|
+
"django_htmx.middleware.HtmxMiddleware",
|
|
94
|
+
]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## HTMX-aware views
|
|
98
|
+
|
|
99
|
+
Importing `htmx_views.views` installs the existing HTMX-aware dispatch hook on
|
|
100
|
+
Django's base `View` class. Mix `HTMXProcessMixin` into a class-based view to
|
|
101
|
+
route HTMX requests to element-specific handlers:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from django.http import HttpResponse
|
|
105
|
+
from django.views.generic import TemplateView
|
|
106
|
+
|
|
107
|
+
from htmx_views.views import HTMXProcessMixin
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class ResultsView(HTMXProcessMixin, TemplateView):
|
|
111
|
+
template_name = "results/page.html"
|
|
112
|
+
template_name_results = "results/_table.html"
|
|
113
|
+
|
|
114
|
+
def htmx_get_refresh(self, request, *args, **kwargs):
|
|
115
|
+
return HttpResponse("refreshed")
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
For an HTMX `GET`, the mixin looks for handlers using the request's
|
|
119
|
+
`trigger_name`, `trigger`, and `target`, in that order. Element names are
|
|
120
|
+
lowercased and stripped of characters other than letters, numbers, and
|
|
121
|
+
underscores. The same convention is available for `DELETE`, `PATCH`, `POST`,
|
|
122
|
+
and `PUT`.
|
|
123
|
+
|
|
124
|
+
`HTMXFormMixin` adds equivalent `htmx_form_valid_<element>` and
|
|
125
|
+
`htmx_form_invalid_<element>` hooks for form views.
|
|
126
|
+
|
|
127
|
+
## Linked selects
|
|
128
|
+
|
|
129
|
+
Linked selects are optional because they require `django-ajax-selects`. After
|
|
130
|
+
installing the `linked-selects` extra, include the package URLs:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from django.urls import include, path
|
|
134
|
+
|
|
135
|
+
urlpatterns = [
|
|
136
|
+
path(
|
|
137
|
+
"htmx-views/",
|
|
138
|
+
include(("htmx_views.urls", "htmx_views"), namespace="htmx_views"),
|
|
139
|
+
),
|
|
140
|
+
]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Then use a registered `django-ajax-selects` lookup:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from django import forms
|
|
147
|
+
|
|
148
|
+
from htmx_views.widgets import HTMXSelectWidget
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class EquipmentForm(forms.Form):
|
|
152
|
+
module = forms.ModelChoiceField(queryset=...)
|
|
153
|
+
category = forms.ModelChoiceField(
|
|
154
|
+
queryset=...,
|
|
155
|
+
widget=HTMXSelectWidget("categories", parent="module"),
|
|
156
|
+
)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The linked-select endpoint runs the lookup's authorisation check before
|
|
160
|
+
querying it. Importing linked-select modules without the optional dependency
|
|
161
|
+
raises an actionable `ImportError`; the core view mixins remain usable.
|
|
162
|
+
|
|
163
|
+
## Migrating an existing project
|
|
164
|
+
|
|
165
|
+
1. Install this package and add `"htmx_views"` to `INSTALLED_APPS`.
|
|
166
|
+
2. Replace imports beginning with `apps.htmx_views` with `htmx_views`.
|
|
167
|
+
3. If linked selects are used, install the extra and include
|
|
168
|
+
`htmx_views.urls` under the `htmx_views` namespace.
|
|
169
|
+
4. Remove the project's copied `apps/htmx_views` directory after its tests
|
|
170
|
+
pass against the package.
|
|
171
|
+
|
|
172
|
+
The former `htmx_views.views.LinkedSelectEndpointView` import remains available
|
|
173
|
+
for compatibility.
|
|
174
|
+
|
|
175
|
+
## Development and builds
|
|
176
|
+
|
|
177
|
+
Create a development environment and run the tests:
|
|
178
|
+
|
|
179
|
+
```console
|
|
180
|
+
python -m pip install -e ".[test,linked-selects]"
|
|
181
|
+
python -m pytest
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Build and validate the wheel and source distribution:
|
|
185
|
+
|
|
186
|
+
```console
|
|
187
|
+
python -m build
|
|
188
|
+
python -m twine check dist/*
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Build the noarch Conda package with `conda-build`:
|
|
192
|
+
|
|
193
|
+
```console
|
|
194
|
+
conda-build conda-recipe
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
The GitHub Actions build workflow runs the tests and produces both Python and
|
|
198
|
+
Conda package artifacts.
|
|
199
|
+
|
|
200
|
+
## Documentation
|
|
201
|
+
|
|
202
|
+
The full documentation is published at
|
|
203
|
+
[gb119.github.io/django-htmx-views](https://gb119.github.io/django-htmx-views/).
|
|
204
|
+
|
|
205
|
+
Build it locally with:
|
|
206
|
+
|
|
207
|
+
```console
|
|
208
|
+
python -m pip install -e ".[docs]"
|
|
209
|
+
python -m sphinx -W --keep-going -b html docs/source docs/build/html
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The documentation workflow rebuilds and deploys GitHub Pages for published
|
|
213
|
+
releases and pre-releases, and can also be started manually.
|
|
214
|
+
|
|
215
|
+
## Publishing a release
|
|
216
|
+
|
|
217
|
+
Creating a GitHub release, including a pre-release, runs the release workflow.
|
|
218
|
+
It verifies that the release tag (for example, `vX.Y.Z`) matches
|
|
219
|
+
`htmx_views.__version__`, reruns the tests, builds both package formats, and
|
|
220
|
+
publishes:
|
|
221
|
+
|
|
222
|
+
- the wheel and source distribution to PyPI;
|
|
223
|
+
- the noarch Conda package to the `phygbu` Anaconda channel with the `main`
|
|
224
|
+
label.
|
|
225
|
+
|
|
226
|
+
Configure these GitHub Actions repository secrets before publishing:
|
|
227
|
+
|
|
228
|
+
- `PYPI_TOKEN`: a PyPI API token authorised for `django-htmx-views`;
|
|
229
|
+
- `ANACONDA`: an Anaconda.org API token authorised to upload to `phygbu`.
|
|
230
|
+
|
|
231
|
+
The upload steps deliberately refuse to overwrite an existing Conda package.
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# django-htmx-views
|
|
2
|
+
|
|
3
|
+
Reusable Django view mixins and widgets for building HTMX-enabled applications.
|
|
4
|
+
This package extracts the `htmx_views` app used by
|
|
5
|
+
[`gb119/labman`](https://github.com/gb119/labman) and
|
|
6
|
+
[`uolphysicsteaching/phas_vitals`](https://github.com/uolphysicsteaching/phas_vitals)
|
|
7
|
+
so that the implementation can be installed and maintained independently.
|
|
8
|
+
|
|
9
|
+
The initial package source is an exact copy of the newer `phas_vitals`
|
|
10
|
+
implementation at commit
|
|
11
|
+
[`1008bc4`](https://github.com/uolphysicsteaching/phas_vitals/commit/1008bc475a28a0ccca40df8769ad6902b68edebc).
|
|
12
|
+
The two upstream apps were compared before extraction and are not currently
|
|
13
|
+
identical; see [UPSTREAM_COMPARISON.md](UPSTREAM_COMPARISON.md) for the
|
|
14
|
+
verification details.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- HTMX-aware dispatch for Django class-based views.
|
|
19
|
+
- Routing by HTMX trigger name, trigger ID, or target ID.
|
|
20
|
+
- HTMX-specific templates, context data, and context object names.
|
|
21
|
+
- HTMX-aware form-valid and form-invalid handlers.
|
|
22
|
+
- Optional linked `<select>` widgets backed by `django-ajax-selects`.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
Install the core package:
|
|
27
|
+
|
|
28
|
+
```console
|
|
29
|
+
python -m pip install django-htmx-views
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Install linked-select support as well:
|
|
33
|
+
|
|
34
|
+
```console
|
|
35
|
+
python -m pip install "django-htmx-views[linked-selects]"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Add the app and `django-htmx` middleware to the host project's settings:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
INSTALLED_APPS = [
|
|
42
|
+
# ...
|
|
43
|
+
"htmx_views",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
MIDDLEWARE = [
|
|
47
|
+
# ...
|
|
48
|
+
"django_htmx.middleware.HtmxMiddleware",
|
|
49
|
+
]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## HTMX-aware views
|
|
53
|
+
|
|
54
|
+
Importing `htmx_views.views` installs the existing HTMX-aware dispatch hook on
|
|
55
|
+
Django's base `View` class. Mix `HTMXProcessMixin` into a class-based view to
|
|
56
|
+
route HTMX requests to element-specific handlers:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from django.http import HttpResponse
|
|
60
|
+
from django.views.generic import TemplateView
|
|
61
|
+
|
|
62
|
+
from htmx_views.views import HTMXProcessMixin
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class ResultsView(HTMXProcessMixin, TemplateView):
|
|
66
|
+
template_name = "results/page.html"
|
|
67
|
+
template_name_results = "results/_table.html"
|
|
68
|
+
|
|
69
|
+
def htmx_get_refresh(self, request, *args, **kwargs):
|
|
70
|
+
return HttpResponse("refreshed")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
For an HTMX `GET`, the mixin looks for handlers using the request's
|
|
74
|
+
`trigger_name`, `trigger`, and `target`, in that order. Element names are
|
|
75
|
+
lowercased and stripped of characters other than letters, numbers, and
|
|
76
|
+
underscores. The same convention is available for `DELETE`, `PATCH`, `POST`,
|
|
77
|
+
and `PUT`.
|
|
78
|
+
|
|
79
|
+
`HTMXFormMixin` adds equivalent `htmx_form_valid_<element>` and
|
|
80
|
+
`htmx_form_invalid_<element>` hooks for form views.
|
|
81
|
+
|
|
82
|
+
## Linked selects
|
|
83
|
+
|
|
84
|
+
Linked selects are optional because they require `django-ajax-selects`. After
|
|
85
|
+
installing the `linked-selects` extra, include the package URLs:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from django.urls import include, path
|
|
89
|
+
|
|
90
|
+
urlpatterns = [
|
|
91
|
+
path(
|
|
92
|
+
"htmx-views/",
|
|
93
|
+
include(("htmx_views.urls", "htmx_views"), namespace="htmx_views"),
|
|
94
|
+
),
|
|
95
|
+
]
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Then use a registered `django-ajax-selects` lookup:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from django import forms
|
|
102
|
+
|
|
103
|
+
from htmx_views.widgets import HTMXSelectWidget
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class EquipmentForm(forms.Form):
|
|
107
|
+
module = forms.ModelChoiceField(queryset=...)
|
|
108
|
+
category = forms.ModelChoiceField(
|
|
109
|
+
queryset=...,
|
|
110
|
+
widget=HTMXSelectWidget("categories", parent="module"),
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The linked-select endpoint runs the lookup's authorisation check before
|
|
115
|
+
querying it. Importing linked-select modules without the optional dependency
|
|
116
|
+
raises an actionable `ImportError`; the core view mixins remain usable.
|
|
117
|
+
|
|
118
|
+
## Migrating an existing project
|
|
119
|
+
|
|
120
|
+
1. Install this package and add `"htmx_views"` to `INSTALLED_APPS`.
|
|
121
|
+
2. Replace imports beginning with `apps.htmx_views` with `htmx_views`.
|
|
122
|
+
3. If linked selects are used, install the extra and include
|
|
123
|
+
`htmx_views.urls` under the `htmx_views` namespace.
|
|
124
|
+
4. Remove the project's copied `apps/htmx_views` directory after its tests
|
|
125
|
+
pass against the package.
|
|
126
|
+
|
|
127
|
+
The former `htmx_views.views.LinkedSelectEndpointView` import remains available
|
|
128
|
+
for compatibility.
|
|
129
|
+
|
|
130
|
+
## Development and builds
|
|
131
|
+
|
|
132
|
+
Create a development environment and run the tests:
|
|
133
|
+
|
|
134
|
+
```console
|
|
135
|
+
python -m pip install -e ".[test,linked-selects]"
|
|
136
|
+
python -m pytest
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Build and validate the wheel and source distribution:
|
|
140
|
+
|
|
141
|
+
```console
|
|
142
|
+
python -m build
|
|
143
|
+
python -m twine check dist/*
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Build the noarch Conda package with `conda-build`:
|
|
147
|
+
|
|
148
|
+
```console
|
|
149
|
+
conda-build conda-recipe
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The GitHub Actions build workflow runs the tests and produces both Python and
|
|
153
|
+
Conda package artifacts.
|
|
154
|
+
|
|
155
|
+
## Documentation
|
|
156
|
+
|
|
157
|
+
The full documentation is published at
|
|
158
|
+
[gb119.github.io/django-htmx-views](https://gb119.github.io/django-htmx-views/).
|
|
159
|
+
|
|
160
|
+
Build it locally with:
|
|
161
|
+
|
|
162
|
+
```console
|
|
163
|
+
python -m pip install -e ".[docs]"
|
|
164
|
+
python -m sphinx -W --keep-going -b html docs/source docs/build/html
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The documentation workflow rebuilds and deploys GitHub Pages for published
|
|
168
|
+
releases and pre-releases, and can also be started manually.
|
|
169
|
+
|
|
170
|
+
## Publishing a release
|
|
171
|
+
|
|
172
|
+
Creating a GitHub release, including a pre-release, runs the release workflow.
|
|
173
|
+
It verifies that the release tag (for example, `vX.Y.Z`) matches
|
|
174
|
+
`htmx_views.__version__`, reruns the tests, builds both package formats, and
|
|
175
|
+
publishes:
|
|
176
|
+
|
|
177
|
+
- the wheel and source distribution to PyPI;
|
|
178
|
+
- the noarch Conda package to the `phygbu` Anaconda channel with the `main`
|
|
179
|
+
label.
|
|
180
|
+
|
|
181
|
+
Configure these GitHub Actions repository secrets before publishing:
|
|
182
|
+
|
|
183
|
+
- `PYPI_TOKEN`: a PyPI API token authorised for `django-htmx-views`;
|
|
184
|
+
- `ANACONDA`: an Anaconda.org API token authorised to upload to `phygbu`.
|
|
185
|
+
|
|
186
|
+
The upload steps deliberately refuse to overwrite an existing Conda package.
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
MIT
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-htmx-views
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reusable Django view services for HTMX-enabled applications
|
|
5
|
+
Author: Gavin Burnell
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/gb119/django-htmx-views
|
|
8
|
+
Project-URL: Repository, https://github.com/gb119/django-htmx-views
|
|
9
|
+
Project-URL: Issues, https://github.com/gb119/django-htmx-views/issues
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Framework :: Django
|
|
12
|
+
Classifier: Framework :: Django :: 4.2
|
|
13
|
+
Classifier: Framework :: Django :: 5.0
|
|
14
|
+
Classifier: Framework :: Django :: 5.1
|
|
15
|
+
Classifier: Framework :: Django :: 5.2
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: Django>=4.2
|
|
30
|
+
Requires-Dist: django-htmx>=1.17
|
|
31
|
+
Provides-Extra: docs
|
|
32
|
+
Requires-Dist: Sphinx<9,>=7; extra == "docs"
|
|
33
|
+
Requires-Dist: django-ajax-selects>=2.2; extra == "docs"
|
|
34
|
+
Requires-Dist: sphinx-better-theme>=0.1.5; extra == "docs"
|
|
35
|
+
Provides-Extra: linked-selects
|
|
36
|
+
Requires-Dist: django-ajax-selects>=2.2; extra == "linked-selects"
|
|
37
|
+
Provides-Extra: test
|
|
38
|
+
Requires-Dist: build>=1.2; extra == "test"
|
|
39
|
+
Requires-Dist: django-ajax-selects>=2.2; extra == "test"
|
|
40
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
41
|
+
Requires-Dist: pytest-django>=4.8; extra == "test"
|
|
42
|
+
Requires-Dist: tomli>=2; python_version < "3.11" and extra == "test"
|
|
43
|
+
Requires-Dist: twine>=5; extra == "test"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# django-htmx-views
|
|
47
|
+
|
|
48
|
+
Reusable Django view mixins and widgets for building HTMX-enabled applications.
|
|
49
|
+
This package extracts the `htmx_views` app used by
|
|
50
|
+
[`gb119/labman`](https://github.com/gb119/labman) and
|
|
51
|
+
[`uolphysicsteaching/phas_vitals`](https://github.com/uolphysicsteaching/phas_vitals)
|
|
52
|
+
so that the implementation can be installed and maintained independently.
|
|
53
|
+
|
|
54
|
+
The initial package source is an exact copy of the newer `phas_vitals`
|
|
55
|
+
implementation at commit
|
|
56
|
+
[`1008bc4`](https://github.com/uolphysicsteaching/phas_vitals/commit/1008bc475a28a0ccca40df8769ad6902b68edebc).
|
|
57
|
+
The two upstream apps were compared before extraction and are not currently
|
|
58
|
+
identical; see [UPSTREAM_COMPARISON.md](UPSTREAM_COMPARISON.md) for the
|
|
59
|
+
verification details.
|
|
60
|
+
|
|
61
|
+
## Features
|
|
62
|
+
|
|
63
|
+
- HTMX-aware dispatch for Django class-based views.
|
|
64
|
+
- Routing by HTMX trigger name, trigger ID, or target ID.
|
|
65
|
+
- HTMX-specific templates, context data, and context object names.
|
|
66
|
+
- HTMX-aware form-valid and form-invalid handlers.
|
|
67
|
+
- Optional linked `<select>` widgets backed by `django-ajax-selects`.
|
|
68
|
+
|
|
69
|
+
## Installation
|
|
70
|
+
|
|
71
|
+
Install the core package:
|
|
72
|
+
|
|
73
|
+
```console
|
|
74
|
+
python -m pip install django-htmx-views
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Install linked-select support as well:
|
|
78
|
+
|
|
79
|
+
```console
|
|
80
|
+
python -m pip install "django-htmx-views[linked-selects]"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Add the app and `django-htmx` middleware to the host project's settings:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
INSTALLED_APPS = [
|
|
87
|
+
# ...
|
|
88
|
+
"htmx_views",
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
MIDDLEWARE = [
|
|
92
|
+
# ...
|
|
93
|
+
"django_htmx.middleware.HtmxMiddleware",
|
|
94
|
+
]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## HTMX-aware views
|
|
98
|
+
|
|
99
|
+
Importing `htmx_views.views` installs the existing HTMX-aware dispatch hook on
|
|
100
|
+
Django's base `View` class. Mix `HTMXProcessMixin` into a class-based view to
|
|
101
|
+
route HTMX requests to element-specific handlers:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from django.http import HttpResponse
|
|
105
|
+
from django.views.generic import TemplateView
|
|
106
|
+
|
|
107
|
+
from htmx_views.views import HTMXProcessMixin
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class ResultsView(HTMXProcessMixin, TemplateView):
|
|
111
|
+
template_name = "results/page.html"
|
|
112
|
+
template_name_results = "results/_table.html"
|
|
113
|
+
|
|
114
|
+
def htmx_get_refresh(self, request, *args, **kwargs):
|
|
115
|
+
return HttpResponse("refreshed")
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
For an HTMX `GET`, the mixin looks for handlers using the request's
|
|
119
|
+
`trigger_name`, `trigger`, and `target`, in that order. Element names are
|
|
120
|
+
lowercased and stripped of characters other than letters, numbers, and
|
|
121
|
+
underscores. The same convention is available for `DELETE`, `PATCH`, `POST`,
|
|
122
|
+
and `PUT`.
|
|
123
|
+
|
|
124
|
+
`HTMXFormMixin` adds equivalent `htmx_form_valid_<element>` and
|
|
125
|
+
`htmx_form_invalid_<element>` hooks for form views.
|
|
126
|
+
|
|
127
|
+
## Linked selects
|
|
128
|
+
|
|
129
|
+
Linked selects are optional because they require `django-ajax-selects`. After
|
|
130
|
+
installing the `linked-selects` extra, include the package URLs:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from django.urls import include, path
|
|
134
|
+
|
|
135
|
+
urlpatterns = [
|
|
136
|
+
path(
|
|
137
|
+
"htmx-views/",
|
|
138
|
+
include(("htmx_views.urls", "htmx_views"), namespace="htmx_views"),
|
|
139
|
+
),
|
|
140
|
+
]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Then use a registered `django-ajax-selects` lookup:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from django import forms
|
|
147
|
+
|
|
148
|
+
from htmx_views.widgets import HTMXSelectWidget
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class EquipmentForm(forms.Form):
|
|
152
|
+
module = forms.ModelChoiceField(queryset=...)
|
|
153
|
+
category = forms.ModelChoiceField(
|
|
154
|
+
queryset=...,
|
|
155
|
+
widget=HTMXSelectWidget("categories", parent="module"),
|
|
156
|
+
)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The linked-select endpoint runs the lookup's authorisation check before
|
|
160
|
+
querying it. Importing linked-select modules without the optional dependency
|
|
161
|
+
raises an actionable `ImportError`; the core view mixins remain usable.
|
|
162
|
+
|
|
163
|
+
## Migrating an existing project
|
|
164
|
+
|
|
165
|
+
1. Install this package and add `"htmx_views"` to `INSTALLED_APPS`.
|
|
166
|
+
2. Replace imports beginning with `apps.htmx_views` with `htmx_views`.
|
|
167
|
+
3. If linked selects are used, install the extra and include
|
|
168
|
+
`htmx_views.urls` under the `htmx_views` namespace.
|
|
169
|
+
4. Remove the project's copied `apps/htmx_views` directory after its tests
|
|
170
|
+
pass against the package.
|
|
171
|
+
|
|
172
|
+
The former `htmx_views.views.LinkedSelectEndpointView` import remains available
|
|
173
|
+
for compatibility.
|
|
174
|
+
|
|
175
|
+
## Development and builds
|
|
176
|
+
|
|
177
|
+
Create a development environment and run the tests:
|
|
178
|
+
|
|
179
|
+
```console
|
|
180
|
+
python -m pip install -e ".[test,linked-selects]"
|
|
181
|
+
python -m pytest
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Build and validate the wheel and source distribution:
|
|
185
|
+
|
|
186
|
+
```console
|
|
187
|
+
python -m build
|
|
188
|
+
python -m twine check dist/*
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Build the noarch Conda package with `conda-build`:
|
|
192
|
+
|
|
193
|
+
```console
|
|
194
|
+
conda-build conda-recipe
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
The GitHub Actions build workflow runs the tests and produces both Python and
|
|
198
|
+
Conda package artifacts.
|
|
199
|
+
|
|
200
|
+
## Documentation
|
|
201
|
+
|
|
202
|
+
The full documentation is published at
|
|
203
|
+
[gb119.github.io/django-htmx-views](https://gb119.github.io/django-htmx-views/).
|
|
204
|
+
|
|
205
|
+
Build it locally with:
|
|
206
|
+
|
|
207
|
+
```console
|
|
208
|
+
python -m pip install -e ".[docs]"
|
|
209
|
+
python -m sphinx -W --keep-going -b html docs/source docs/build/html
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The documentation workflow rebuilds and deploys GitHub Pages for published
|
|
213
|
+
releases and pre-releases, and can also be started manually.
|
|
214
|
+
|
|
215
|
+
## Publishing a release
|
|
216
|
+
|
|
217
|
+
Creating a GitHub release, including a pre-release, runs the release workflow.
|
|
218
|
+
It verifies that the release tag (for example, `vX.Y.Z`) matches
|
|
219
|
+
`htmx_views.__version__`, reruns the tests, builds both package formats, and
|
|
220
|
+
publishes:
|
|
221
|
+
|
|
222
|
+
- the wheel and source distribution to PyPI;
|
|
223
|
+
- the noarch Conda package to the `phygbu` Anaconda channel with the `main`
|
|
224
|
+
label.
|
|
225
|
+
|
|
226
|
+
Configure these GitHub Actions repository secrets before publishing:
|
|
227
|
+
|
|
228
|
+
- `PYPI_TOKEN`: a PyPI API token authorised for `django-htmx-views`;
|
|
229
|
+
- `ANACONDA`: an Anaconda.org API token authorised to upload to `phygbu`.
|
|
230
|
+
|
|
231
|
+
The upload steps deliberately refuse to overwrite an existing Conda package.
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT
|