Skip to main content

A fast, HTML aware, Django template formatter, written in Rust.

Project description

Djangofmt

Pypi Version License Supported Python Versions Actions status pre-commit.ci status

A fast, HTML aware, Django template formatter, written in Rust.

Heavily rely on the awesome markup_fmt with some additions to support Django fully.

Installation

djangofmt is available on PyPI.

# With pip
pip install djangofmt

# With uv
uv tool install djangofmt@latest  # Install djangofmt globally.
uv add --dev djangofmt            # Or add djangofmt to your project.

# With pipx
pipx install djangofmt

As a pre-commit hook

See pre-commit for instructions

Sample .pre-commit-config.yaml:

- repo: https://github.com/UnknownPlatypus/djangofmt-pre-commit
  rev: v0.2.1
  hooks:
    - id: djangofmt

The separate repository enables installation without compiling the Rust code.

By default, the configuration uses pre-commit’s files option to detect all text files in directories named templates. If your templates are stored elsewhere, you can override this behavior by specifying the desired files in the hook configuration within your .pre-commit-config.yaml file.

Usage

Usage: djangofmt [OPTIONS] <FILES>...

Arguments:
  <FILES>...
          List of files to format

Options:
      --line-length <LINE_LENGTH>
          Set the line-length [default: 120]
      --profile <PROFILE>
          Template language profile to use [default: django] [possible values: django, jinja]
      --custom-blocks <BLOCK_NAMES>
          Comma-separated list of custom block name to enable
  -h, --help
          Print help
  -V, --version
          Print version

Djangofmt 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, git ls-files | xargs:

git ls-files -z -- '*.html' | xargs -0r djangofmt

…or PowerShell’s ForEach-Object:

git ls-files -- '*.html' | %{djangofmt $_}

Controlling the formatting

DjangoFmt gives users control over formatting in cases where static analysis struggles to determine the optimal approach.

Splitting an opening tag across multiple lines

You can control this formatting by choosing whether to insert a newline before the first attribute:

# Unchanged
<div class="flex" id="great" data-a>
  This is nice!
</div>

# Wrap on multiple lines
<div
-    class="flex" id="great" data-a>
+    class="flex"
+    id="great"
+    data-a
+>
    This is nice!
</div>

Class attribute formatting

The class attribute will be formatted as a space-separated sequence of strings, unless there are already newlines inside the attribute value.

This makes it possible to accommodate the 2 following use cases:

<div class="
  mt-8 p-8
  bg-indigo-600 hover:bg-indigo-700
  border border-transparent
  font-medium text-white
">
    Hello world
</div>

<div class="mt-8 p-8 bg-indigo-600 hover:bg-indigo-700 border border-transparent font-medium text-white">
    Hello world
</div>

See https://github.com/g-plane/markup_fmt/issues/75#issuecomment-2456526352 for the rationale.

Known limitations

style attributes formatting

The style attribute will be formatted using a CSS formatter (Malva), but the output will always be on a single line.

Before:

<div class="flex flex-col items-center absolute z-10"
     style="top:60%;
            transform:translate(0,-50%)">
    Such a lovely day
</div>

After:

<div class="flex flex-col items-center absolute z-10"
     style="top:60%; transform:translate(0,-50%)">
    Such a lovely day
</div>

Conditional open/close tags

Djangofmt doesn't accept and will produce parsing errors for any syntax that could cut off HTML in obvious ways, e.g.:

{% if condition %}
    <div class="container">
{% endif %}
    Some content
{% if condition %}
    </div>
{% endif %}

This is generally discouraged and should be avoided because it's an easy way to create invalid HTML.

See upstream tracking issue: https://github.com/g-plane/markup_fmt/issues/97

Benchmarks

Here are the results benchmarking djangofmt against similar tools on 100k lines of HTML across 1.7k files.

Shows a bar chart with benchmark results.

Formatting 100k+ lines of HTML across 1.7k+ files from scratch.

This is important to note that only djlint covers the same scope in terms of formatting capabilities. djade only alter django templating, djhtml only fix indentation and prettier only understand html (and will break templates)

As always, these results should be taken with a grain of salt. Results on my machine will differ from yours, especially if you have many CPU cores because some tools take better advantage of parallelization than others.

But at least it was fun to build thanks to the wonderful hyperfine tool.

Benchmark details (2025-02-28)

This was run on my AMD Ryzen 9 7950X (32) @ 5.881GHz.

Tools versions:

  • djangofmt: v0.1.0
  • prettier: v3.5.2
  • djlint: v1.36.4
  • djade: v1.3.2
  • djhtml: v3.0.7
Benchmark 1: cat /tmp/test-files | xargs --max-procs=0 ../../target/release/djangofmt format --profile django --line-length 120 --quiet
  Time (mean ± σ):      19.8 ms ±   0.9 ms    [User: 179.6 ms, System: 73.7 ms]
  Range (min … max):    18.3 ms …  23.3 ms    73 runs

  Warning: Ignoring non-zero exit code.

Benchmark 2: cat /tmp/test-files | xargs --max-procs=0 djade --target-version 5.1
  Time (mean ± σ):      72.0 ms ±   1.0 ms    [User: 63.2 ms, System: 9.3 ms]
  Range (min … max):    70.5 ms …  73.4 ms    18 runs

Benchmark 3: cat /tmp/test-files | xargs --max-procs=0 djhtml
  Time (mean ± σ):      1.401 s ±  0.026 s    [User: 1.322 s, System: 0.079 s]
  Range (min … max):    1.373 s …  1.453 s    10 runs

Benchmark 4: cat /tmp/test-files | xargs --max-procs=0 djlint --reformat --profile=django --max-line-length 120
  Time (mean ± σ):      2.343 s ±  0.026 s    [User: 64.944 s, System: 1.176 s]
  Range (min … max):    2.297 s …  2.377 s    10 runs

  Warning: Ignoring non-zero exit code.

Benchmark 5: cat /tmp/test-files | xargs --max-procs=0 ./node_modules/.bin/prettier --ignore-unknown --write --print-width 120 --log-level silent
  Time (mean ± σ):      3.226 s ±  0.062 s    [User: 4.481 s, System: 0.261 s]
  Range (min … max):    3.092 s …  3.292 s    10 runs

  Warning: Ignoring non-zero exit code.

Summary
  cat /tmp/test-files | xargs --max-procs=0 ../../target/release/djangofmt format --profile django --line-length 120 --quiet ran
    3.63 ± 0.17 times faster than cat /tmp/test-files | xargs --max-procs=0 djade --target-version 5.1
   70.71 ± 3.45 times faster than cat /tmp/test-files | xargs --max-procs=0 djhtml
  118.28 ± 5.48 times faster than cat /tmp/test-files | xargs --max-procs=0 djlint --reformat --profile=django --max-line-length 120
  162.80 ± 7.96 times faster than cat /tmp/test-files | xargs --max-procs=0 ./node_modules/.bin/prettier --ignore-unknown --write --print-width 120 --log-level silent

Shell Completions

You can generate shell completions for your preferred shell using the djangofmt completions command.

Usage: djangofmt completions <SHELL>

Arguments:
  <SHELL>
      The shell to generate the completions for
      [possible values: bash, elvish, fish, nushell, powershell, zsh]

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

djangofmt-0.2.1.tar.gz (74.4 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

djangofmt-0.2.1-py3-none-win_arm64.whl (1.4 MB view details)

Uploaded Python 3Windows ARM64

djangofmt-0.2.1-py3-none-win_amd64.whl (1.5 MB view details)

Uploaded Python 3Windows x86-64

djangofmt-0.2.1-py3-none-win32.whl (1.3 MB view details)

Uploaded Python 3Windows x86

djangofmt-0.2.1-py3-none-musllinux_1_2_x86_64.whl (1.6 MB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

djangofmt-0.2.1-py3-none-musllinux_1_2_i686.whl (1.6 MB view details)

Uploaded Python 3musllinux: musl 1.2+ i686

djangofmt-0.2.1-py3-none-musllinux_1_2_armv7l.whl (1.4 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARMv7l

djangofmt-0.2.1-py3-none-musllinux_1_2_aarch64.whl (1.4 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARM64

djangofmt-0.2.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.6 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

djangofmt-0.2.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl (1.6 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ s390x

djangofmt-0.2.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl (1.7 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ppc64le

djangofmt-0.2.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl (1.7 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ppc64

djangofmt-0.2.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl (1.6 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ i686

djangofmt-0.2.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl (1.4 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARMv7l

djangofmt-0.2.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.4 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

djangofmt-0.2.1-py3-none-macosx_11_0_arm64.whl (1.4 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

djangofmt-0.2.1-py3-none-macosx_10_12_x86_64.whl (1.5 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

djangofmt-0.2.1-py3-none-linux_armv6l.whl (1.4 MB view details)

Uploaded Python 3

File details

Details for the file djangofmt-0.2.1.tar.gz.

File metadata

  • Download URL: djangofmt-0.2.1.tar.gz
  • Upload date:
  • Size: 74.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.12

File hashes

Hashes for djangofmt-0.2.1.tar.gz
Algorithm Hash digest
SHA256 b4baa6e30292311d9a21d40f93559f3a6a855699c95648d38396aac2181df929
MD5 4aec110cd678024d5478bca287df7865
BLAKE2b-256 0cded5b0cf84aee66a6c82d9acab4d0a1dce4aa40bf3e6da53857858df4a08fe

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-win_arm64.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 199143c2a4dcfc6e8ca2081d5077db0b77f94970a6bc35ad2a3260dbe21f59e9
MD5 6e2bb144628ea7b1ce17ba68e713d73f
BLAKE2b-256 68866cca0165ef2b7c8e92faa12626706c30d665d74eacb5b2def3cb305a3809

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-win_amd64.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 02cf89c7f57ac8942522138851d3c38e846e6a02dd2e60a9ee1b2440d4acea9e
MD5 716e0a6e94e8265000a8b0ada421f4a3
BLAKE2b-256 92a75c28c54b89ea3853c013e508e6f1398e4ce4aa4e3efe1842b009c4556892

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-win32.whl.

File metadata

  • Download URL: djangofmt-0.2.1-py3-none-win32.whl
  • Upload date:
  • Size: 1.3 MB
  • Tags: Python 3, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.12

File hashes

Hashes for djangofmt-0.2.1-py3-none-win32.whl
Algorithm Hash digest
SHA256 eab8bec4ef0e23ac38b40dc7c990bcb719b94aa59331335eb4a62aed511d71d3
MD5 e1145855deafd87cab486b050846301b
BLAKE2b-256 1d52899b88844d8e94bac1173bce4d1039827cab060c1dfcb6809354a854313a

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 455ec1027ad509e0655f28f926d3d25d01b855a23b36e060513028e88a666c85
MD5 a6beb05522f5be0e20b6b2f7781ea90b
BLAKE2b-256 bf02b9a16cf8f6f98e47459469a5260a066fe325442455a58e69da0fb3591ce1

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-musllinux_1_2_i686.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 c11d44839089b9a3e344d158dee38b6c1ad26e1452078dcb379fa7f4cb889006
MD5 651adf07094152a06b363fd58ba14877
BLAKE2b-256 014fc17d6c80d7de8ffd76e31fe5f2aa7fe1b55ccf0b7ec7133877e827606792

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-musllinux_1_2_armv7l.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 0269514cfbfa7a4c697dcec3d643443276921e3ec7eaec7c2bccc1a3b64c88cf
MD5 f4911ad0805a411f2ad4bbbd95e50a05
BLAKE2b-256 c94ab27cfb3952bb1a74e43b5692d774bc3aa2c8ab63eae0b180ccf4d7d099fc

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 8f6aefec0eaf862947a6059c484497e08415181d59451fe32f53d528041e9c91
MD5 76c22d48c7aef138f5a48290800fda9a
BLAKE2b-256 d75ba4df25427bcca5a907f3311c4050667efaf6974c9439ccafba575f511c17

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7732d6a4cea17a39f0b5084edf4600e80dc7b1982dc7d154cb9a6165871de7ba
MD5 d42f270126251c563d2f6a1ec54e6ed6
BLAKE2b-256 2a4a7925ac99bd997935e3cab57ca6b9a504f0461161ccdc031455abeff50422

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl
Algorithm Hash digest
SHA256 986182e92a3b22f316fb59302ec58fccea65be4fe46bd5f911de8092e7f55460
MD5 b74e351e680de43b8e44154af487f13f
BLAKE2b-256 f0870463b2f3e2b8eea4208cff26d5b5e319f7c6dd4817d8518d0ad353795f4a

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl
Algorithm Hash digest
SHA256 eda198d41388c6ba2ffa642f51844dffb98ea8034c08a8342b37bcfa7a6808ac
MD5 8f7e80acdc842dff84d3a3a1fef6de77
BLAKE2b-256 926181ec2de7ed639980399a9d8b911eadb5250fa79c212a8503b563f57351d1

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl
Algorithm Hash digest
SHA256 750dac15b7bf88c003ce2f9a83b4723e7d89096245eed0e2b4f378bde94ea183
MD5 3021cdd5d25cb9c66b2fbcc983cbe764
BLAKE2b-256 670eb1ce30fd740699313a33e23f8e123678a08afddf6d0fa5b6f6e07908447c

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 84866bae259fa4ef63e80b0619b8507ddc7ee32db971a36e01f3203cc906be24
MD5 c6b6be13f39100b5eee642b1c863fd59
BLAKE2b-256 3fb044fc4eeca2d75a781602168c0104d1cc446f7f1ae58f8da94adcbde84984

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
Algorithm Hash digest
SHA256 5f14aa0c8b1e5753c55d9d131ba4d5165b7c3d8aa8ce8d86a3528186edc32ffc
MD5 ef901f53b9472911f10c7b66cb7886dd
BLAKE2b-256 8c183dd8bdcd8403819213263564f3dd4a99e2661c3df5d545feb50785f015cb

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 b4f613bc8729786aa66ed72ff46a5d777ba62a226dc33aa2d3f927b68e158ccf
MD5 c4a1528355de0f3ecbcd952ba3961674
BLAKE2b-256 97b86297c9681835657bc67f7606311b44ab49629595321721245a2d317fe0e1

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 eaadd15460c9253bfeefa85d7494c1b3f11d9f19dc6e709135ffa67be4f41d72
MD5 8bba7332bb03eb2854a327b3fcc24458
BLAKE2b-256 4d312ab0f6a01750475272209b45b645a35c0bd5ec44d0fee582a1f685a0d4da

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 237d9cb8476aa2fe96e34c0b93a1ce45e9f0ece9eb310686e46ab00a4d049307
MD5 ec30cf37d6f8568316a21fec90ff672c
BLAKE2b-256 48030baba13e7667efd079d953db8ba256eef15e07e3b64b8ac63a7f0b95f08d

See more details on using hashes here.

File details

Details for the file djangofmt-0.2.1-py3-none-linux_armv6l.whl.

File metadata

File hashes

Hashes for djangofmt-0.2.1-py3-none-linux_armv6l.whl
Algorithm Hash digest
SHA256 0dc4baa5a70312fe4bb7ba55e92de8b4168389e5bfc4551782963703dc508cff
MD5 84981e5b3a9839737a2c6a70f571c726
BLAKE2b-256 65148f3dcd89d8cdaf49f419f54ca76456bf6c5d7de2e2f8b935b6a04a6d89cc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page