===================
django-htmx-include
===================
Render a template using htmx with the current context.
----
| ``hx-get`` is often used to delegate potentially computationally expensive template fragments to ``htmx``.
Achieving this sometimes requires the creation of multiple views, each of which needs to inherit from mixins that
provide access to the same context.
| ``django-htmx-include`` provides a template tag that aims to make this easier by leveraging the cache,
removing the need for the extra views.
----
Requirements
------------
* Python 3.10 to 3.12 supported.
* Django 4.2 to 5.0 supported.
* `htmx <https://htmx.org/>`__
Setup
-----
* Install from **pip**:
.. code-block:: sh
python -m pip install django-htmx-include
* Add it to your installed apps:
.. code-block:: python
INSTALLED_APPS = [
...,
"django_htmx_include",
...,
]
* Include the app URLs in your root URLconf:
.. code-block:: python
from django.urls import include, path
urlpatterns = [
...,
path("__htmx-include__/", include("django_htmx_include.urls")),
]
You can use another prefix if required.
Usage
-----
Once installed, load the ``xinclude`` library and use the tag passing the template that you want to include:
.. code-block:: html
{% load xinclude %}
{% xinclude "footer.html" %}{% endxinclude %}
Every feature of the regular |include|__ tag is supported, including the use of ``with`` and ``only``.
.. |include| replace:: ``include``
__ https://docs.djangoproject.com/en/dev/ref/templates/builtins/#include
You can use the following htmx-specfic arguments:
* ``hx-trigger``: corresponds to the |hx-trigger|__ htmx attribute. Defaults to ``load once``.
.. |hx-trigger| replace:: ``hx-trigger``
__ https://htmx.org/attributes/hx-trigger/
* ``swap-time``: corresponds to the ``swap`` timing of the |hx-swap|__ htmx attribute.
.. |hx-swap| replace:: ``hx-swap``
__ https://htmx.org/attributes/hx-swap/#timing-swap-settle
* ``settle-time``: corresponds to the ``settle`` timing of the |hx-swap|__ htmx attribute.
__ https://htmx.org/attributes/hx-swap/#timing-swap-settle
"Primary nodes" may be passed along to render initial content prior to htmx swapping. For example:
.. code-block:: html
{% xinclude "footer.html" %}
<div>Loading...</div>
{% endxinclude %}
``django-htmx-include`` plays well with the excellent `django-template-partials <https://github.com/carltongibson/django-template-partials/>`__
package, to select specific partials on the target template.
Advanced usage
^^^^^^^^^^^^^^
Below is a more complete example making use of the htmx `transition classes <https://htmx.org/examples/animations/#swapping>`__.
Note the ``intersect once`` trigger, which will fire the request once when the element intersects the viewport.
.. code-block:: html
<style>
.htmx-swapping > #loading {
opacity: 0;
transition: opacity 1s ease-out;
}
</style>
{% xinclude "magic.html" hx-trigger="intersect once" swap-time="1s" settle-time="1s" %}
<div id="loading">
Loading...
</div>
{% endxinclude %}
``magic.html``:
.. code-block:: html
<style>
#crystal.htmx-added {
opacity: 1;
animation: appear ease-in 500ms;
}
</style>
<div id="crystal">
🔮
</div>
How It Works
------------
``django-htmx-include`` first checks if it needs to render the target template synchronously;
see the `Rendering synchronously <#rendering-synchronously>`__ section for cases where this might be useful.
If this is not the case, it stores the current context and the target template to the cache and constructs a url
with a ``fragment_id`` that targets an internal view. It then renders a parent ``div`` element containing all the
necessary htmx attributes. Once the htmx request fires, the view fetches the cache context and template that match
the passed ``fragment_id`` and uses that context to render the template.
Cache
^^^^^
``django-htmx-include`` uses either the cache that corresponds to the ``HTMX_INCLUDE_CACHE_ALIAS`` setting, if specified,
or the ``CACHES["default"]``.
When setting a new cache key, it finds unpickable values and discards them.
If you want to see which keys get discarded, update your ``settings.LOGGERS`` to include ``"htmx_include"``
with ``"level": "DEBUG"``.
| All official `Django cache backends <https://docs.djangoproject.com/en/5.0/ref/settings/#backend>`__ should work,
under one **important condition**:
| Your cache should be accessible from all your app instances. If you are using multi-processing for your Django application,
or multiple servers clusters, make sure that your ``django-htmx-include`` cache is accessible from all the instances,
otherwise your requests will result in 404s.
Authorization
^^^^^^^^^^^^^
The request user is expected to be the one that initially accessed the original view (and added to cache),
or ``AnonymousUser`` in both cases; otherwise ``django-htmx-include`` will return 404 for the htmx requests.
If ``request.user`` is not available, for instance when ``django.contrib.auth`` is not in the ``INSTALLED_APPS``,
then ``django-htmx-include`` assumes that the end user can access the data.
Rendering synchronously
^^^^^^^^^^^^^^^^^^^^^^^
There are cases where you might want to conditionally render fragments synchronously (i.e. use the regular ``include``).
For example, you could render synchronously for SEO purposes, when robots are crawling your pages, but still make use
of the htmx functionality for regular users. ``django-htmx-include`` support this, it checks on a ``htmx_sync_include``
attribute on the request and renders synchronously if that evaluates to ``True``.
So you can add a custom middleware that sets the ``htmx_sync_include`` attribute upon your individual conditions.
See also `Configuration <#configuration>`__ below for the ``HTMX_SYNC_INCLUDE_REQUEST_ATTR`` setting.
Configuration
-------------
``HTMX_INCLUDE_CACHE_ALIAS: str``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The cache alias that ``django-htmx-include`` will use, it defaults to ``CACHES["default"]``.
``HTMX_INCLUDE_CACHE_TIMEOUT: int``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The number of seconds that contexts should be stored in the cache. If the setting is not present, Django will
use the default timeout argument of the appropriate backend in the ``CACHES`` setting.
``HTMX_SYNC_INCLUDE_REQUEST_ATTR: str``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The request attribute that ``django-htmx-include`` will check on to determine if it needs to render synchronously.
It defaults to ``htmx_sync_include``.
Running the tests
-----------------
Fork, then clone the repo:
.. code-block:: sh
git clone git@github.com:your-username/django-htmx-include.git
Set up a venv:
.. code-block:: sh
python -m venv .venv
source .venv/bin/activate
python -m pip install -e .[tests,dev]
Set up the |pre-commit|__ hooks:
.. |pre-commit| replace:: ``pre-commit``
__ https://pre-commit.com/
.. code-block:: sh
pre-commit install
Then you can run the tests with the |just|__ command runner:
.. |just| replace:: ``just``
__ https://github.com/casey/just
.. code-block:: sh
just test
Or with coverage:
.. code-block:: sh
just coverage
If you don't have ``just`` installed, you can look in the ``justfile`` for the
commands that are run.
|
Complementary packages
----------------------
* |django-htmx|__: Extensions for using Django with htmx.
* |django-template-partials|__: Reusable named inline partials for the Django Template Language.
.. |django-htmx| replace:: ``django-htmx``
__ https://github.com/adamchainz/django-htmx
.. |django-template-partials| replace:: ``django-template-partials``
__ https://github.com/carltongibson/django-template-partials/
Raw data
{
"_id": null,
"home_page": null,
"name": "django-htmx-include",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "django, templates, htmx, xhr, cache",
"author": null,
"author_email": "Giannis Terzopoulos <terzo.giannis@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/e8/ef/26a9fd86b0d15b36fa6aa22318bf7a53f3fee5f8a1d9ed6acc7725d2d52e/django_htmx_include-0.1.0.tar.gz",
"platform": null,
"description": "===================\ndjango-htmx-include\n===================\n\nRender a template using htmx with the current context.\n\n----\n\n| ``hx-get`` is often used to delegate potentially computationally expensive template fragments to ``htmx``.\n Achieving this sometimes requires the creation of multiple views, each of which needs to inherit from mixins that\n provide access to the same context.\n| ``django-htmx-include`` provides a template tag that aims to make this easier by leveraging the cache,\n removing the need for the extra views.\n\n----\n\nRequirements\n------------\n* Python 3.10 to 3.12 supported.\n* Django 4.2 to 5.0 supported.\n* `htmx <https://htmx.org/>`__\n\nSetup\n-----\n\n* Install from **pip**:\n\n.. code-block:: sh\n\n python -m pip install django-htmx-include\n\n* Add it to your installed apps:\n\n.. code-block:: python\n\n INSTALLED_APPS = [\n ...,\n \"django_htmx_include\",\n ...,\n ]\n\n* Include the app URLs in your root URLconf:\n\n.. code-block:: python\n\n from django.urls import include, path\n\n urlpatterns = [\n ...,\n path(\"__htmx-include__/\", include(\"django_htmx_include.urls\")),\n ]\n\nYou can use another prefix if required.\n\n\nUsage\n-----\n\nOnce installed, load the ``xinclude`` library and use the tag passing the template that you want to include:\n\n.. code-block:: html\n\n {% load xinclude %}\n\n {% xinclude \"footer.html\" %}{% endxinclude %}\n\nEvery feature of the regular |include|__ tag is supported, including the use of ``with`` and ``only``.\n\n.. |include| replace:: ``include``\n__ https://docs.djangoproject.com/en/dev/ref/templates/builtins/#include\n\nYou can use the following htmx-specfic arguments:\n\n* ``hx-trigger``: corresponds to the |hx-trigger|__ htmx attribute. Defaults to ``load once``.\n\n.. |hx-trigger| replace:: ``hx-trigger``\n__ https://htmx.org/attributes/hx-trigger/\n\n* ``swap-time``: corresponds to the ``swap`` timing of the |hx-swap|__ htmx attribute.\n\n.. |hx-swap| replace:: ``hx-swap``\n__ https://htmx.org/attributes/hx-swap/#timing-swap-settle\n\n* ``settle-time``: corresponds to the ``settle`` timing of the |hx-swap|__ htmx attribute.\n\n__ https://htmx.org/attributes/hx-swap/#timing-swap-settle\n\n\"Primary nodes\" may be passed along to render initial content prior to htmx swapping. For example:\n\n.. code-block:: html\n\n {% xinclude \"footer.html\" %}\n <div>Loading...</div>\n {% endxinclude %}\n\n``django-htmx-include`` plays well with the excellent `django-template-partials <https://github.com/carltongibson/django-template-partials/>`__\npackage, to select specific partials on the target template.\n\nAdvanced usage\n^^^^^^^^^^^^^^\nBelow is a more complete example making use of the htmx `transition classes <https://htmx.org/examples/animations/#swapping>`__.\nNote the ``intersect once`` trigger, which will fire the request once when the element intersects the viewport.\n\n.. code-block:: html\n\n <style>\n .htmx-swapping > #loading {\n opacity: 0;\n transition: opacity 1s ease-out;\n }\n </style>\n\n {% xinclude \"magic.html\" hx-trigger=\"intersect once\" swap-time=\"1s\" settle-time=\"1s\" %}\n <div id=\"loading\">\n Loading...\n </div>\n {% endxinclude %}\n\n``magic.html``:\n\n.. code-block:: html\n\n <style>\n #crystal.htmx-added {\n opacity: 1;\n animation: appear ease-in 500ms;\n }\n </style>\n\n <div id=\"crystal\">\n \ud83d\udd2e\n </div>\n\nHow It Works\n------------\n``django-htmx-include`` first checks if it needs to render the target template synchronously;\nsee the `Rendering synchronously <#rendering-synchronously>`__ section for cases where this might be useful.\nIf this is not the case, it stores the current context and the target template to the cache and constructs a url\nwith a ``fragment_id`` that targets an internal view. It then renders a parent ``div`` element containing all the\nnecessary htmx attributes. Once the htmx request fires, the view fetches the cache context and template that match\nthe passed ``fragment_id`` and uses that context to render the template.\n\nCache\n^^^^^\n``django-htmx-include`` uses either the cache that corresponds to the ``HTMX_INCLUDE_CACHE_ALIAS`` setting, if specified,\nor the ``CACHES[\"default\"]``.\nWhen setting a new cache key, it finds unpickable values and discards them.\nIf you want to see which keys get discarded, update your ``settings.LOGGERS`` to include ``\"htmx_include\"``\nwith ``\"level\": \"DEBUG\"``.\n\n| All official `Django cache backends <https://docs.djangoproject.com/en/5.0/ref/settings/#backend>`__ should work,\n under one **important condition**:\n| Your cache should be accessible from all your app instances. If you are using multi-processing for your Django application,\n or multiple servers clusters, make sure that your ``django-htmx-include`` cache is accessible from all the instances,\n otherwise your requests will result in 404s.\n\nAuthorization\n^^^^^^^^^^^^^\nThe request user is expected to be the one that initially accessed the original view (and added to cache),\nor ``AnonymousUser`` in both cases; otherwise ``django-htmx-include`` will return 404 for the htmx requests.\nIf ``request.user`` is not available, for instance when ``django.contrib.auth`` is not in the ``INSTALLED_APPS``,\nthen ``django-htmx-include`` assumes that the end user can access the data.\n\nRendering synchronously\n^^^^^^^^^^^^^^^^^^^^^^^\nThere are cases where you might want to conditionally render fragments synchronously (i.e. use the regular ``include``).\nFor example, you could render synchronously for SEO purposes, when robots are crawling your pages, but still make use\nof the htmx functionality for regular users. ``django-htmx-include`` support this, it checks on a ``htmx_sync_include``\nattribute on the request and renders synchronously if that evaluates to ``True``.\nSo you can add a custom middleware that sets the ``htmx_sync_include`` attribute upon your individual conditions.\n\nSee also `Configuration <#configuration>`__ below for the ``HTMX_SYNC_INCLUDE_REQUEST_ATTR`` setting.\n\nConfiguration\n-------------\n\n``HTMX_INCLUDE_CACHE_ALIAS: str``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nThe cache alias that ``django-htmx-include`` will use, it defaults to ``CACHES[\"default\"]``.\n\n``HTMX_INCLUDE_CACHE_TIMEOUT: int``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nThe number of seconds that contexts should be stored in the cache. If the setting is not present, Django will\nuse the default timeout argument of the appropriate backend in the ``CACHES`` setting.\n\n``HTMX_SYNC_INCLUDE_REQUEST_ATTR: str``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nThe request attribute that ``django-htmx-include`` will check on to determine if it needs to render synchronously.\nIt defaults to ``htmx_sync_include``.\n\nRunning the tests\n-----------------\n\nFork, then clone the repo:\n\n.. code-block:: sh\n\n git clone git@github.com:your-username/django-htmx-include.git\n\nSet up a venv:\n\n.. code-block:: sh\n\n python -m venv .venv\n source .venv/bin/activate\n python -m pip install -e .[tests,dev]\n\nSet up the |pre-commit|__ hooks:\n\n.. |pre-commit| replace:: ``pre-commit``\n__ https://pre-commit.com/\n\n.. code-block:: sh\n\n pre-commit install\n\nThen you can run the tests with the |just|__ command runner:\n\n.. |just| replace:: ``just``\n__ https://github.com/casey/just\n\n.. code-block:: sh\n\n just test\n\nOr with coverage:\n\n.. code-block:: sh\n\n just coverage\n\nIf you don't have ``just`` installed, you can look in the ``justfile`` for the\ncommands that are run.\n\n|\n\nComplementary packages\n----------------------\n* |django-htmx|__: Extensions for using Django with htmx.\n* |django-template-partials|__: Reusable named inline partials for the Django Template Language.\n\n.. |django-htmx| replace:: ``django-htmx``\n__ https://github.com/adamchainz/django-htmx\n\n.. |django-template-partials| replace:: ``django-template-partials``\n__ https://github.com/carltongibson/django-template-partials/\n",
"bugtrack_url": null,
"license": null,
"summary": "django-htmx-include",
"version": "0.1.0",
"project_urls": {
"Changelog": "https://github.com/g-nie/django-htmx-include/blob/main/CHANGELOG.rst",
"Repository": "https://github.com/g-nie/django-htmx-include"
},
"split_keywords": [
"django",
" templates",
" htmx",
" xhr",
" cache"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "94020111faf075bed5ac67c43c18578cfb8bfe0a74aefa005cdc03fd858cfa52",
"md5": "6ea033f3dac8aedbfc71ffa2f4a37c95",
"sha256": "fce3b9c967193b8244b779826519e3a6e296f5072b5a3f0ed86700f31ac96bae"
},
"downloads": -1,
"filename": "django_htmx_include-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "6ea033f3dac8aedbfc71ffa2f4a37c95",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 12695,
"upload_time": "2024-06-17T13:35:16",
"upload_time_iso_8601": "2024-06-17T13:35:16.433636Z",
"url": "https://files.pythonhosted.org/packages/94/02/0111faf075bed5ac67c43c18578cfb8bfe0a74aefa005cdc03fd858cfa52/django_htmx_include-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "e8ef26a9fd86b0d15b36fa6aa22318bf7a53f3fee5f8a1d9ed6acc7725d2d52e",
"md5": "06d5e27ac71b6e69c9b54d0c75057ddc",
"sha256": "e8dbbeda6b9bd7b3a48f5820292886d7eaffc3e2cb580bcc4b8f6d9669b5a1fd"
},
"downloads": -1,
"filename": "django_htmx_include-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "06d5e27ac71b6e69c9b54d0c75057ddc",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 17863,
"upload_time": "2024-06-17T13:35:18",
"upload_time_iso_8601": "2024-06-17T13:35:18.892942Z",
"url": "https://files.pythonhosted.org/packages/e8/ef/26a9fd86b0d15b36fa6aa22318bf7a53f3fee5f8a1d9ed6acc7725d2d52e/django_htmx_include-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-06-17 13:35:18",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "g-nie",
"github_project": "django-htmx-include",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "django-htmx-include"
}