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.2
  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

.svg files support

Djangofmt can format svg files too. It will behave exactly the same way as if they were html files.

There is a dedicated pre-commit for these:

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

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.2.tar.gz (74.8 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.2-py3-none-win_arm64.whl (1.4 MB view details)

Uploaded Python 3Windows ARM64

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

Uploaded Python 3Windows x86-64

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

Uploaded Python 3Windows x86

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

Uploaded Python 3musllinux: musl 1.2+ x86-64

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

Uploaded Python 3musllinux: musl 1.2+ i686

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

Uploaded Python 3musllinux: musl 1.2+ ARMv7l

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

Uploaded Python 3musllinux: musl 1.2+ ARM64

djangofmt-0.2.2-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.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl (1.6 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ s390x

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

Uploaded Python 3manylinux: glibc 2.17+ ppc64le

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

Uploaded Python 3manylinux: glibc 2.17+ ppc64

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

Uploaded Python 3manylinux: glibc 2.17+ i686

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

Uploaded Python 3manylinux: glibc 2.17+ ARMv7l

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

Uploaded Python 3manylinux: glibc 2.17+ ARM64

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

Uploaded Python 3macOS 11.0+ ARM64

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

Uploaded Python 3macOS 10.12+ x86-64

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

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for djangofmt-0.2.2.tar.gz
Algorithm Hash digest
SHA256 ba426a94a397f629143a655f2344ac9c622d0ea8946872f95a05ddb61d2e263b
MD5 e915ae73a8ee28349c8cd29e639b4139
BLAKE2b-256 94dac84328b92fde3a6e539d373acfa05717328332d14d04aeda5c32df5b2d13

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 38c057f4fb8b4b1e1fb4561b5d8a99b06eae4ddf8fb1ffd62296b5e8951aa269
MD5 e56ebb256bd4913cd61de52875a69bfb
BLAKE2b-256 3b049a02f6fb418e919de7fa4a0d0087be54b662e55032a6deded2812893e86a

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 afb30870714e33523550555f2fa438e3132e0bdca81e043ebdcb428f3707871d
MD5 a09bc3828c9fc2cf891e2a98a14a3e1d
BLAKE2b-256 3f95e474c73340dd9444e98e63dd2a807cb47ccff1fa333ca1659b49300ff4ca

See more details on using hashes here.

File details

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

File metadata

  • Download URL: djangofmt-0.2.2-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.2-py3-none-win32.whl
Algorithm Hash digest
SHA256 2f625c9f9ec9fd1b5a1b073fada0cb953e96753a49c7f85d0217b463839f08bc
MD5 f909092c641493681371ed542b9ea691
BLAKE2b-256 20ba15344905fa53552eacec19731bbe041d109271cd4b4f49ae305f063ac69e

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 dc40baa589c6c49a4f6138a8b36c8498398d57193bc6cec6c0e424c2e388e15e
MD5 082a244e16ee2db806ee6c59e74ff2c0
BLAKE2b-256 e3a924118c39bb95293fe8684faa963194be5653bf56af293e9bf06c7483378b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 bd0906188706388d624680d58f9556984374adc20d4da11dcc560609d2895a11
MD5 976df11b797278bd393af2cc180c9959
BLAKE2b-256 ba6d5bf32ae5497c6f9adb35fce18f836d3e244f6e0bbe13ebaa9923de4acc12

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 a887073d62e75a2fb4f27e8c70f8201c6bc293d55eba7a7a093ba5513d3a55b3
MD5 3243bb4c34961f5c36991ae02eb64029
BLAKE2b-256 286fa84e1e08a6abd6156758cbf503c543daf7e73cbff8447305b2db543c7e02

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 d9e3e11efbb8a05eeb4421756cf07b66ab51a34ade66bf4bc55c54ffa3f62a84
MD5 0cb1d7754bf9d645cc5f87d37865ac02
BLAKE2b-256 91960abb2282cb3ad4377ac2c2c4a759c56bba186dfe27357f695f94fb842db9

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 1cc833799c0295e3a70480215e76d9c498f0e6441fb7e539843a5c1a756ae281
MD5 a96797e4611e8498e99bfefb4cb05216
BLAKE2b-256 9696d817bb6db9518153f9cc5faf0d63ec953b51d81ee2529f81266801f67269

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl
Algorithm Hash digest
SHA256 597ce6eb81be1b05f0696d3db4c94b562f4f0b79b1c8550a424cbecd7e38d7ee
MD5 fcf0ed22fc004aca38a0ac314ac2e9d7
BLAKE2b-256 c330c9e0edc53bfac798bafad319e2356fe0c1b188d3981b75158b69c0a4becd

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl
Algorithm Hash digest
SHA256 46f6d3d5b40102b06759e0ec4f7d0f5114789c8139c17609874f1d052dd8edcd
MD5 c1a50287f30848f72964b1ead3dab179
BLAKE2b-256 5629cd6e4d92d49727fff89393c2f8c2fa37df023a9a770dcd16b5868ce60ed5

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl
Algorithm Hash digest
SHA256 bf4d962da2301dcd40b787d7c4c36b43a2387ee61cbf962396c65a17506a1116
MD5 2c6aaaf59b6f1b971716879f4bdefd33
BLAKE2b-256 ae7011f4b7bd61a5bdefe8389c65857ae7525729cd931c7de29f320231a15862

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 96b7a57a0faaf547d3590349d4cebce7ccb7feedcc6cf84dd3b05a15d541c24b
MD5 d56a11ed4b1f1944feec65363e1f78ad
BLAKE2b-256 b70903afacf8c777163e659d4719d5b81b80c0f946379c53c91cdfed4aa63d72

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
Algorithm Hash digest
SHA256 0b521b42e4537662c6c2e612a7ae8284b5c748dc1898aa1638c638c8e930f28b
MD5 b652814ad545ccb58f5e75db5b3c39c1
BLAKE2b-256 d021c2d89a1467697c1bf85b9295b9e90efe135204c7b3a4df16e6e9f9fc931f

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 1ccad24d483867b077a85791a2c35083a3a1bcb2d9e62e731b5a3848d7ee3b9d
MD5 64964d0e7cab827eeeeb0d2fb08d1bb0
BLAKE2b-256 d50ef220740e0844dd81e15c6336195db360bda3259100d7da49f9dabab10dbd

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c2847e6ab7bb85de5f83f690ac00985b681f43bb31f66f8aae65f33743f779e6
MD5 f3e50dc65f9b2969acf5a4c01752a19f
BLAKE2b-256 af966e24b8abdb019f3bff026616fec5207c3e4cbbe0bc5baeaab67575258c83

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 bf33222ac141ea0bba67bf0e86fb64c7679b67f2ff6abe0ed07691c53016d0df
MD5 c9e0d2fa1392f2a8b27947f719be8582
BLAKE2b-256 e66c3b09dddee8f010b53199fcc4548553ae026c51114d5795647046a86679be

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for djangofmt-0.2.2-py3-none-linux_armv6l.whl
Algorithm Hash digest
SHA256 60a64f8fb3d119c0c1742ae61140c6ffea5c25b0565633b8d86d6c82d0a311d8
MD5 141eb9ed8e962f461536f950312c7ebe
BLAKE2b-256 b86aea2375e821f40c8263a9a7d5297b2154b8d1185e5188ca7a11ef515ec0f6

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