https://github.com/adamchainz/djade
A Django template formatter.
https://github.com/adamchainz/djade
django
Last synced: 6 months ago
JSON representation
A Django template formatter.
- Host: GitHub
- URL: https://github.com/adamchainz/djade
- Owner: adamchainz
- License: mit
- Created: 2024-09-20T12:32:38.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-04-07T18:09:12.000Z (6 months ago)
- Last Synced: 2025-04-11T14:33:43.466Z (6 months ago)
- Topics: django
- Language: Rust
- Homepage:
- Size: 157 KB
- Stars: 321
- Watchers: 7
- Forks: 5
- Open Issues: 13
-
Metadata Files:
- Readme: README.rst
- Changelog: CHANGELOG.rst
- License: LICENSE
- Code of conduct: .github/CODE_OF_CONDUCT.md
- Security: .github/SECURITY.md
Awesome Lists containing this project
README
=====
Djade
=====.. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/djade/main.yml.svg?branch=main&style=for-the-badge
:target: https://github.com/adamchainz/djade/actions?workflow=CI.. image:: https://img.shields.io/pypi/v/djade.svg?style=for-the-badge
:target: https://pypi.org/project/djade/.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge
:target: https://github.com/pre-commit/pre-commit
:alt: pre-commit.. figure:: https://raw.githubusercontent.com/adamchainz/djade/main/logo.svg
:alt: You can have any colour you like, as long as it’s jade...
A Django template formatter.
You can have any colour you like, as long as it’s [d]jade.
Djade formats Django template syntax with a style based on the `template style guide `__ in Django’s documentation.
It does not format HTML or other templated languages.Djade is fast because it is built in Rust: benchmarked taking 20ms to format 377 templates.
Read more in `the introductory post `__, or below.
----
**Improve your Django and Git skills** with `my books `__.
----
Installation
============Use **pip**:
.. code-block:: sh
python -m pip install djade
Python 3.9 to 3.13 supported.
pre-commit hook
---------------You can also install Djade as a `pre-commit `__ hook.
**First,** add the following to the ``repos`` section of your ``.pre-commit-config.yaml`` file (`docs `__):
.. code-block:: yaml
- repo: https://github.com/adamchainz/djade-pre-commit
rev: "" # Replace with the latest tag on GitHub
hooks:
- id: djade
args: [--target-version, "5.2"] # Replace with Django versionThe separate repository enables installation without compiling the Rust code.
The default configuration uses pre-commit’s |files option|__ to pick up all text files in directories called ``templates`` (`source `__).
You may wish to override this if you have templates in different directories by adding ``files`` to the hook configuration in your ``.pre-commit-config.yaml`` file... |files option| replace:: ``files`` option
__ https://pre-commit.com/#creating-new-hooks**Second,** format your entire project:
.. code-block:: sh
pre-commit run djade --all-files
Check these changes for any potential Djade bugs and commit them.
Try ``git diff --ignore-all-space`` to check non-whitespace changes.**Third,** consider adding the previous commit SHA to a |.git-blame-ignore-revs file|__.
This will prevent the initial formatting commit from showing up in ``git blame``... |.git-blame-ignore-revs file| replace:: ``.git-blame-ignore-revs`` file
__ https://docs.github.com/en/repositories/working-with-files/using-files/viewing-a-file#ignore-commits-in-the-blame-viewKeep the hook installed to continue formatting your templates.
pre-commit’s ``autoupdate`` command will upgrade Djade so you can take advantage of future features.Usage
=====``djade`` is a command line tool that rewrites files in place.
Pass a list of template files to format them:.. code-block:: console
$ djade --target-version 5.2 templates/engine.html
1 file reformattedDjade can also upgrade some old template syntax.
Add the ``--target-version`` option with your Django version as ``.`` to enable applicable fixers:.. code-block:: console
$ djade --target-version 5.2 templates/engine.html
1 file reformattedDjade does not have any ability to recurse through directories.
Use the pre-commit integration, globbing, or another technique to apply it to many files.
For example, |with git ls-files pipe xargs|_:.. |with git ls-files pipe xargs| replace:: with ``git ls-files | xargs``
.. _with git ls-files pipe xargs: https://adamj.eu/tech/2022/03/09/how-to-run-a-command-on-many-files-in-your-git-repository/.. code-block:: sh
git ls-files -z -- '*.html' | xargs -0r djade
…or PowerShell’s |ForEach-Object|__:
.. |ForEach-Object| replace:: ``ForEach-Object``
__ https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/foreach-object.. code-block:: powershell
git ls-files -- '*.html' | %{djade $_}
Options
=======``--target-version``
--------------------Optional: the version of Django to target, in the format ``.``.
If provided, Djade enables its fixers for versions up to and including the target version.
See the list of available versions with ``djade --help``.``--check``
-----------Avoid writing any formatted files back.
Instead, exit with a non-zero status code if any files would have been modified, and zero otherwise.Formatting
==========Djade aims to format Django template syntax in a consistent, clean way.
It wants to be like `Black `__: opinionated and free of configuration.
Djade’s style is based on the rules listed in the Django contribution style guide’s `template style section `__, plus some more.Djade does not aim to format the host language of templates (HTML, etc.).
That is a much broader scope and hard to do without semantic changes.
For example, whitespace is significant in some HTML contexts, such as in ```` tags, so even adjusting indentation can affect the meaning.Below are the rules that Djade implements.
Rules from the Django style guide:
* Single spaces at the start and end of variables and tags:
.. code-block:: diff
-{{train}}
+{{ train }}-{% blow whistle %}
+{% blow whistle %}* Label ``{% endblock %}`` tags that aren’t on the same line as their opening ``{% block %}`` tag:
.. code-block:: diff
{% block funnel %}
...
-{% endblock %}
+{% endblock funnel %}* Sort libraries in ``{% load %}`` tags:
.. code-block:: diff
-{% load coal boiler %}
+{% load boiler coal %}* Inside variables, no spaces around filters:
.. code-block:: diff
-{{ fire | stoke }}
+{{ fire|stoke }}* Inside tags, single spaces between tokens:
.. code-block:: diff
-{% if locomotive == 'steam engine' %}
+{% if locomotive == 'steam engine' %}* Unindent top-level ``{% block %}`` and ``{% endblock %}`` tags when ``{% extends %}`` is used:
.. code-block:: diff
- {% extends 'engine.html' %}
+{% extends 'engine.html' %}- {% block boiler %}
+{% block boiler %}
...
- {% endblock boiler %}
+{% endblock boiler %}Extra rules:
* No leading empty lines:
.. code-block:: diff
-
{% extends 'engine.html' %}
...* No trailing empty lines:
.. code-block:: diff
...
{% endblock wheels %}
-
-* Single spaces at the start and end of comments:
.. code-block:: diff
-{#choo choo#}
+{# choo choo #}* No labels in ``{% endblock %}`` tags on the same line as their opening ``{% block %}`` tag:
.. code-block:: diff
-{% block funnel %}...{% endblock funnel %}
+{% block funnel %}...{% endblock %}* Merge consecutive ``{% load %}`` tags:
.. code-block:: diff
-{% load boiler %}
-
-{% load coal %}
+{% load boiler coal %}* Sort loaded items in ``{% load ... from .. %}`` tags:
.. code-block:: diff
-{% load steam heat from boiler %}
+{% load heat steam from boiler %}* Unindent ``{% extends %}`` tags:
.. code-block:: diff
- {% extends 'engine.html' %}
+{% extends 'engine.html' %}* Exactly one blank line between top-level ``{% block %}`` and ``{% endblock %}`` tags when ``{% extends %}`` is used:
.. code-block:: diff
{% extends 'engine.html' %}
-
{% block funnel %}
...
{% endblock funnel %}
+
{% block boiler %}
...
{% endblock boiler %}Fixers
======Djade applies the below fixes based on the target Django version from ``--target-version``.
Django 4.2+: ``length_is`` -> ``length``
----------------------------------------From the `release note `__:
The ``length_is`` template filter is deprecated in favor of ``length`` and the ``==`` operator within an ``{% if %}`` tag.
Djade updates usage of the deprecated filter within ``if`` tags, without other conditions, appropriately:
.. code-block:: diff
-{% if engines|length_is:1 %}
+{% if engines|length == 1 %}Django 4.1+: empty ID ``json_script`` fixer
-------------------------------------------From the `release note `__:
The HTML ```` element ``id`` attribute is no longer required when wrapping the ``json_script`` template filter.
Djade removes the argument where ``json_script`` is passed an empty string, to avoid emitting ``id=""``:
.. code-block:: diff
-{% tracks|json_script:"" %}
+{% tracks|json_script %}Django 3.1+: ``trans`` -> ``translate``, ``blocktrans`` / ``endblocktrans`` -> ``blocktranslate`` / ``endblocktranslate``
-------------------------------------------------------------------------------------------------------------------------From the `release note <https://docs.djangoproject.com/en/3.1/releases/3.1/#templates>`__:
The renamed ``translate`` and ``blocktranslate`` template tags are introduced for internationalization in template code.
The older ``trans`` and ``blocktrans`` template tags aliases continue to work, and will be retained for the foreseeable future.Djade updates the deprecated tags appropriately:
.. code-block:: diff
-{% load blocktrans trans from i18n %}
+{% load blocktranslate translate from i18n %}-{% trans "Engine colours" %}
+{% translate "Engine colours" %}-{% blocktrans with colour=engine.colour %}
+{% blocktranslate with colour=engine.colour %}
This engine is {{ colour }}.
-{% endblocktrans %}
+{% endblocktranslate %}Django 3.1+: ``ifequal`` and ``ifnotequal`` -> ``if``
-----------------------------------------------------From the `release note <https://docs.djangoproject.com/en/3.1/releases/3.1/#id2:~:text=The%20%7B%25%20ifequal%20%25%7D%20and%20%7B%25%20ifnotequal%20%25%7D%20template%20tags>`__:
The ``{% ifequal %}`` and ``{% ifnotequal %}`` template tags are deprecated in favor of ``{% if %}``.
Djade updates the deprecated tags appropriately:
.. code-block:: diff
-{% ifequal engine.colour 'blue' %}
+{% if engine.colour == 'blue' %}
Thomas!
-{% endifequal %}
+{% endif %}-{% ifnotequal engine.colour 'blue' %}
+{% if engine.colour != 'blue' %}
Not Thomas.
-{% endifnotequal %}
+{% endif %}Django 2.1+: ``admin_static`` and ``staticfiles`` -> ``static``
---------------------------------------------------------------From the `release note <https://docs.djangoproject.com/en/2.1/releases/2.1/#features-deprecated-in-2-1>`__:
``{% load staticfiles %}`` and ``{% load admin_static %}`` are deprecated in favor of ``{% load static %}``, which works the same.
Djade updates ``{% load %}`` tags appropriately:
.. code-block:: diff
-{% load staticfiles %}
+{% load static %}-{% load admin_static %}
+{% load static %}Django 1.3+: legacy variable assignment syntax
----------------------------------------------The minimum target Django version is 2.1, so this fixer is always active.
Django 1.3 added support for ``=`` to assign variables in ``{% with %}`` and ``{% blocktranslate %}`` tags.
Prior to this, they only supported the legacy syntax using the ``as`` keyword, which Django still supports.Djade rewrites the older ``as`` syntax to the newer ``=`` one:
.. code-block:: diff
-{% with engines.count as total %}
+{% with total=engines.count %}
...
{% endwith %}-{% blocktranslate with engine.colour as colour %}
+{% blocktranslate with colour=engine.colour %}
...
{% endblocktranslate %}