django-xinclude


Namedjango-xinclude JSON
Version 0.1.1 PyPI version JSON
download
home_pageNone
Summarydjango-xinclude
upload_time2024-06-19 20:35:26
maintainerNone
docs_urlNone
authorNone
requires_python>=3.10
licenseNone
keywords django templates htmx xhr cache
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ===================
django-xinclude
===================

.. image:: https://img.shields.io/pypi/v/django-xinclude.svg
   :target: https://pypi.org/project/django-xinclude/
   :alt: PyPI version

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 more views, each of which needs to inherit from mixins that
  provide access to the same context.
| ``django-xinclude`` provides a template tag that aims to make this easier by leveraging the cache,
  removing the need for the 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-xinclude

* Add it to your installed apps:

.. code-block:: python

    INSTALLED_APPS = [
        ...,
        "django_xinclude",
        ...,
    ]

* Include the app URLs in your root URLconf:

.. code-block:: python

   from django.urls import include, path

   urlpatterns = [
       ...,
       path("__xinclude__/", include("django_xinclude.urls")),
   ]

You can use a different 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-specific 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-xinclude`` 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" with wand="🪄" hx-trigger="intersect once" swap-time="1s" settle-time="1s" %}
        <div id="loading">
            Loading...
        </div>
    {% endxinclude %}

``magic.html``:

.. code-block:: html

    <style>
    #items.htmx-added {
        opacity: 1;
        animation: appear ease-in 500ms;
    }
    </style>

    <div id="items">
        🔮 {{ wand }}
    </div>

----

You can preload the ``xinclude`` libary in every template by appending to your ``TEMPLATES`` ``builtins`` setting.
This way you don't need to repeat the ``{% load xinclude %}`` in every template that you need the tag:

.. code-block:: python

    TEMPLATES = [
        {
            "BACKEND": "django.template.backends.django.DjangoTemplates",
            ...,
            "OPTIONS": {
                "builtins": [
                    "django_xinclude.templatetags.xinclude",
                ],
            },
        },
    ]


How It Works
------------
``django-xinclude`` first checks if it needs to render the target template synchronously;
see the `Section below <#rendering-synchronously>`__ 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-xinclude`` uses either the cache that corresponds to the ``XINCLUDE_CACHE_ALIAS`` setting, if specified,
or ``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 ``"django_xinclude"``
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-xinclude`` 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-xinclude`` 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-xinclude`` 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-xinclude`` supports this, it checks for a ``xinclude_sync``
attribute on the request and renders synchronously if that evaluates to ``True``.
So you can add a custom middleware that sets the ``xinclude_sync`` attribute upon your individual conditions.

See also `Configuration <#configuration>`__ below for the ``XINCLUDE_SYNC_REQUEST_ATTR`` setting.

Configuration
-------------

``XINCLUDE_CACHE_ALIAS: str``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The cache alias that ``django-xinclude`` will use, it defaults to ``CACHES["default"]``.

``XINCLUDE_CACHE_TIMEOUT: int``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The number of seconds that contexts will remain in cache. If the setting is not present, Django will
use the default timeout argument of the appropriate backend in the ``CACHES`` setting.

``XINCLUDE_SYNC_REQUEST_ATTR: str``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The request attribute that ``django-xinclude`` will check on to determine if it needs to render synchronously.
It defaults to ``xinclude_sync``.

Running the tests
-----------------

Fork, then clone the repo:

.. code-block:: sh

    git clone git@github.com:your-username/django-xinclude.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-xinclude",
    "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/be/9f/d20896924a67425f53c87102faccefc5babd8193821e51ebe089c075abd4/django_xinclude-0.1.1.tar.gz",
    "platform": null,
    "description": "===================\ndjango-xinclude\n===================\n\n.. image:: https://img.shields.io/pypi/v/django-xinclude.svg\n   :target: https://pypi.org/project/django-xinclude/\n   :alt: PyPI version\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 more views, each of which needs to inherit from mixins that\n  provide access to the same context.\n| ``django-xinclude`` provides a template tag that aims to make this easier by leveraging the cache,\n  removing the need for the 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-xinclude\n\n* Add it to your installed apps:\n\n.. code-block:: python\n\n    INSTALLED_APPS = [\n        ...,\n        \"django_xinclude\",\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(\"__xinclude__/\", include(\"django_xinclude.urls\")),\n   ]\n\nYou can use a different 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-specific 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-xinclude`` 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\" with wand=\"\ud83e\ude84\" 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    #items.htmx-added {\n        opacity: 1;\n        animation: appear ease-in 500ms;\n    }\n    </style>\n\n    <div id=\"items\">\n        \ud83d\udd2e {{ wand }}\n    </div>\n\n----\n\nYou can preload the ``xinclude`` libary in every template by appending to your ``TEMPLATES`` ``builtins`` setting.\nThis way you don't need to repeat the ``{% load xinclude %}`` in every template that you need the tag:\n\n.. code-block:: python\n\n    TEMPLATES = [\n        {\n            \"BACKEND\": \"django.template.backends.django.DjangoTemplates\",\n            ...,\n            \"OPTIONS\": {\n                \"builtins\": [\n                    \"django_xinclude.templatetags.xinclude\",\n                ],\n            },\n        },\n    ]\n\n\nHow It Works\n------------\n``django-xinclude`` first checks if it needs to render the target template synchronously;\nsee the `Section below <#rendering-synchronously>`__ 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-xinclude`` uses either the cache that corresponds to the ``XINCLUDE_CACHE_ALIAS`` setting, if specified,\nor ``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 ``\"django_xinclude\"``\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-xinclude`` 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-xinclude`` 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-xinclude`` 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-xinclude`` supports this, it checks for a ``xinclude_sync``\nattribute on the request and renders synchronously if that evaluates to ``True``.\nSo you can add a custom middleware that sets the ``xinclude_sync`` attribute upon your individual conditions.\n\nSee also `Configuration <#configuration>`__ below for the ``XINCLUDE_SYNC_REQUEST_ATTR`` setting.\n\nConfiguration\n-------------\n\n``XINCLUDE_CACHE_ALIAS: str``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nThe cache alias that ``django-xinclude`` will use, it defaults to ``CACHES[\"default\"]``.\n\n``XINCLUDE_CACHE_TIMEOUT: int``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nThe number of seconds that contexts will remain in cache. If the setting is not present, Django will\nuse the default timeout argument of the appropriate backend in the ``CACHES`` setting.\n\n``XINCLUDE_SYNC_REQUEST_ATTR: str``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nThe request attribute that ``django-xinclude`` will check on to determine if it needs to render synchronously.\nIt defaults to ``xinclude_sync``.\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-xinclude.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-xinclude",
    "version": "0.1.1",
    "project_urls": {
        "Changelog": "https://github.com/g-nie/django-xinclude/blob/main/CHANGELOG.rst",
        "Repository": "https://github.com/g-nie/django-xinclude"
    },
    "split_keywords": [
        "django",
        " templates",
        " htmx",
        " xhr",
        " cache"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "72570e24837b868219e98045dff8095076cc6edb5ecdbda6c98eb1bbc61e1c98",
                "md5": "07479bb10cad12fdacac1eb5d37c6832",
                "sha256": "aa8a6960266c2ec95220879a83bcd34a4f789e96000484dc5511aee13cefe003"
            },
            "downloads": -1,
            "filename": "django_xinclude-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "07479bb10cad12fdacac1eb5d37c6832",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 12721,
            "upload_time": "2024-06-19T20:35:24",
            "upload_time_iso_8601": "2024-06-19T20:35:24.248747Z",
            "url": "https://files.pythonhosted.org/packages/72/57/0e24837b868219e98045dff8095076cc6edb5ecdbda6c98eb1bbc61e1c98/django_xinclude-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "be9fd20896924a67425f53c87102faccefc5babd8193821e51ebe089c075abd4",
                "md5": "d02908c11a6ded4827c0acf24d53b456",
                "sha256": "41a251de99c2ef41145c2ab826487d18bd09742fc91bd00286e0de008ba2e66d"
            },
            "downloads": -1,
            "filename": "django_xinclude-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "d02908c11a6ded4827c0acf24d53b456",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 18680,
            "upload_time": "2024-06-19T20:35:26",
            "upload_time_iso_8601": "2024-06-19T20:35:26.216513Z",
            "url": "https://files.pythonhosted.org/packages/be/9f/d20896924a67425f53c87102faccefc5babd8193821e51ebe089c075abd4/django_xinclude-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-06-19 20:35:26",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "g-nie",
    "github_project": "django-xinclude",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "django-xinclude"
}
        
Elapsed time: 0.28138s