Skip to main content

Static N+1 query detector for Django ORM – fast Rust-powered checker

Project description

django-check

Static N+1 query detection for Django (LSP-based)

django-check is a static analyzer and Language Server that detects N+1 query patterns in Django code before runtime. It inspects queryset construction and related-field access to warn when relations are accessed without proper prefetching (select_related, prefetch_related).

image

It works inside the editor or directly from CLI.

[!WARNING] This project is in active development and not yet production-ready.

  • APIs are unstable
  • Diagnostics may be incomplete or incorrect
  • Expect breaking changes without notice

Why this exists

Django’s ORM makes it easy to accidentally introduce N+1 queries that:

  • pass tests,
  • look correct in code review,
  • only show up under load.

Runtime tools (django-silk, nplusone) are focuesd in runtime optimiezation. django-check make static analysis before the runtime.

Key properties

  • Compute the graph of the Models in the app
  • Zero runtime overhead
  • LSP-based diagnostics at edit time
  • No code instrumentation required
  • Works with any editor that supports LSP

What it detects (today)

  • Iteration over QuerySets followed by related-field access

  • Missing select_related / prefetch_related for:

    • ForeignKey
    • OneToOne
    • ManyToMany
    • Inheritance
    • Reverse relations (with related_name explicit or not)
    • Complex chained relations like model__relation__child_relation__depth
    • Prefetch usage: Prefetch("relation", queryset=Relation.objects.select_related("child")

Installation

pip install djch

Or with uv:

uv pip install djch

This installs the djch binary to your PATH.

CLI

Usage: djch <COMMAND>

Commands:
  server  Start as a Language Server (normally handled by the IDE)
  check   Analyze the current directory tree for N+1 queries
  help    Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help (see more with '--help')
  -V, --version  Print version

Check from CLI using djch check. You will get an output like this:

app/foo/bar/views/tier1.py:210:22: [N+1] rp.ticker_benchmark
Potential N+1 query: accessing `rp.ticker_benchmark` inside loop

app/apps/crawler/tasks.py:48:17: [N+1] ticker.industry
Potential N+1 query: accessing `ticker.industry` inside loop

app/apps/crawler/views.py:62:20: [N+1] stream.streamer
Potential N+1 query: accessing `stream.streamer` inside loop

app/apps/foo/selectors/anointed.py:43:19: [N+1] anointed.pattern
Potential N+1 query: accessing `anointed.pattern` inside loop

Editor integration (LSP)

Neovim 0.11+ (native LSP)

Neovim 0.11 ships with a stable built-in LSP client.

Minimal setup:

-- lua/init.lua
vim.lsp.enable("djch")
-- lsp/djch.lua
return {
  cmd = { "djch", "server" },
  filetypes = { "python" },
  root_markers = { 'manage.py', 'pyproject.toml', '.git' }
}

This registers django-check as a first-class LSP server.

If you are already attaching multiple LSPs to Python buffers (e.g. Pyright), Neovim will merge diagnostics correctly.

VS Code (Extension)

You can install the VSCode extension from, or directly in VSCode Extensions Market Place.

https://marketplace.visualstudio.com/items?itemName=richardhapb.Django-Check

Examples

Problematic code

# N+1 query detected
users = User.objects.all()
profiles = [user.profile in user for users]  # N+1

Explanation:

  • users is evaluated once
  • user.profile triggers one query per iteration

Corrected code

users = User.objects.select_related("profile").all()
profiles = [user.profile in user for users]

Problematic code

users = User.objects.all()
for user in users:
    user.profile.bio # N+1

Explanation:

  • users is evaluated once
  • user.profile triggers one query per iteration

Corrected code

users = User.objects.select_related("profile").all()
for user in users:
    user.profile.bio

django-check will clear the diagnostic once the relation is prefetched.

How it works (high level)

  1. Parse Python source into an AST
  2. Identify Django model classes and relationships
  3. Track QuerySet-producing expressions
  4. Track iteration boundaries
  5. Detect attribute access that implies ORM resolution
  6. Verify whether the required relation is prefetched

Design philosophy

  • Zero config
  • Just works out of the box
  • Editor feedback must be actionable, not noisy

Limitations (work in progress)

  • Interprocedural analysis requires type hints on QuerySet parameters
  • Limited understanding of:
    • annotate, aggregate
    • complex custom managers

Roadmap

  • Custom queryset method summaries
  • Templates integration

Contributing

This project lives at the intersection of:

  • Python AST
  • Django ORM semantics
  • LSP protocol design

If you are interested in any of those, contributions are welcome.

Documentation contributions are welcome, the goal is make this tool easy to use.

License

MIT

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

djch-0.1.8.tar.gz (40.6 kB view details)

Uploaded Source

Built Distributions

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

djch-0.1.8-py3-none-win_arm64.whl (2.4 MB view details)

Uploaded Python 3Windows ARM64

djch-0.1.8-py3-none-win32.whl (2.3 MB view details)

Uploaded Python 3Windows x86

djch-0.1.8-py3-none-musllinux_1_2_i686.whl (2.5 MB view details)

Uploaded Python 3musllinux: musl 1.2+ i686

djch-0.1.8-py3-none-musllinux_1_2_armv7l.whl (2.4 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARMv7l

djch-0.1.8-py3-none-musllinux_1_2_aarch64.whl (2.4 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARM64

djch-0.1.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl (2.6 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ s390x

djch-0.1.8-py3-none-macosx_11_0_arm64.whl (2.3 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

djch-0.1.8-py3-none-macosx_10_12_x86_64.whl (2.4 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

Details for the file djch-0.1.8.tar.gz.

File metadata

  • Download URL: djch-0.1.8.tar.gz
  • Upload date:
  • Size: 40.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for djch-0.1.8.tar.gz
Algorithm Hash digest
SHA256 3a6dec65c00c207d8bbbc833cee1bbc7e8ab1e46ddeedf3893eab7ca4e73ec6c
MD5 0a2d501e98a369cc380e66b1120706ea
BLAKE2b-256 39780baef466ac95f71268f74e66d9da4391a52e44d1db574b7cdede6798cb0c

See more details on using hashes here.

File details

Details for the file djch-0.1.8-py3-none-win_arm64.whl.

File metadata

  • Download URL: djch-0.1.8-py3-none-win_arm64.whl
  • Upload date:
  • Size: 2.4 MB
  • Tags: Python 3, Windows ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for djch-0.1.8-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 44b106e82f0ea4a85098639f08e9704c69e8c7c284354bca25f0aaa2cd60cebd
MD5 30edb58529bd0a74986bbfa8d7bc72b4
BLAKE2b-256 c2c4332253dfc6cba5cb9d4f8aeae87e3658af7469adfda25882166ada191de5

See more details on using hashes here.

File details

Details for the file djch-0.1.8-py3-none-win32.whl.

File metadata

  • Download URL: djch-0.1.8-py3-none-win32.whl
  • Upload date:
  • Size: 2.3 MB
  • Tags: Python 3, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for djch-0.1.8-py3-none-win32.whl
Algorithm Hash digest
SHA256 acfa1c0cfa3eb017f8976ada6ba4e0b735d6d0f74bd3502ae02cfe93534a3872
MD5 bf52a301d09a8eeb09c8873516cad68a
BLAKE2b-256 4bedede1ff36c94fc0ce315e8f1160c56838ee64d368ac59eb77ae84e066296e

See more details on using hashes here.

File details

Details for the file djch-0.1.8-py3-none-musllinux_1_2_i686.whl.

File metadata

  • Download URL: djch-0.1.8-py3-none-musllinux_1_2_i686.whl
  • Upload date:
  • Size: 2.5 MB
  • Tags: Python 3, musllinux: musl 1.2+ i686
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for djch-0.1.8-py3-none-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 944c7d4d036527b40fba977ff6c8bde5fde7915a8481d1bba6ea9a50e0193c50
MD5 cc8f8d2459d65309d45bb2f134ed3fcc
BLAKE2b-256 06ad80532cecdbfda808e9b128603dcd95d73ff5e44a2d585235574518fd8a17

See more details on using hashes here.

File details

Details for the file djch-0.1.8-py3-none-musllinux_1_2_armv7l.whl.

File metadata

  • Download URL: djch-0.1.8-py3-none-musllinux_1_2_armv7l.whl
  • Upload date:
  • Size: 2.4 MB
  • Tags: Python 3, musllinux: musl 1.2+ ARMv7l
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for djch-0.1.8-py3-none-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 00432432fc85a65e43dfa71ab24d01715e294295751c0597c2dc228f2efef103
MD5 c44d3b3fec0fd11d83774c511709ffc4
BLAKE2b-256 c05e85da38deef56d41c604eb113412bc8760aa19be467f0a97638f7762a555b

See more details on using hashes here.

File details

Details for the file djch-0.1.8-py3-none-musllinux_1_2_aarch64.whl.

File metadata

  • Download URL: djch-0.1.8-py3-none-musllinux_1_2_aarch64.whl
  • Upload date:
  • Size: 2.4 MB
  • Tags: Python 3, musllinux: musl 1.2+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for djch-0.1.8-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 8d59334018f388955f2848cb2a0f5f6b36c987d75377e4b5d08b86142c3ddad3
MD5 f96e04a1dbc0dd78472927a90cd6a447
BLAKE2b-256 1315353a02deeeea2c5abe72130da1769325bdf7e03f7bd9cb4a08eaec4d8886

See more details on using hashes here.

File details

Details for the file djch-0.1.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl.

File metadata

  • Download URL: djch-0.1.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl
  • Upload date:
  • Size: 2.6 MB
  • Tags: Python 3, manylinux: glibc 2.17+ s390x
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for djch-0.1.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl
Algorithm Hash digest
SHA256 ff788f0994c0db49eb4bee5d5f71f85c3b7cfc90551bb434bcad9bcdae4fffba
MD5 51f43d92e8d08b9b59dc05a33f3f4063
BLAKE2b-256 feb85179559dc88f7987de88f75b050f1b5181ed4be6c3c5f9ab591d379dda32

See more details on using hashes here.

File details

Details for the file djch-0.1.8-py3-none-macosx_11_0_arm64.whl.

File metadata

  • Download URL: djch-0.1.8-py3-none-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 2.3 MB
  • Tags: Python 3, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for djch-0.1.8-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4372705d7307ae1acbe8b2a62447a6ed604199eed5861a83eb163bd05166d2f2
MD5 c99042aa434bb2cc1b485f455d25c082
BLAKE2b-256 45710c774c74799d4c095662fbda34c227f61092ab64524af5034e3b04738959

See more details on using hashes here.

File details

Details for the file djch-0.1.8-py3-none-macosx_10_12_x86_64.whl.

File metadata

  • Download URL: djch-0.1.8-py3-none-macosx_10_12_x86_64.whl
  • Upload date:
  • Size: 2.4 MB
  • Tags: Python 3, macOS 10.12+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.3 {"installer":{"name":"uv","version":"0.10.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for djch-0.1.8-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 ff8b41a2dd3c6a78db4de2e6e3ccb503ffcd6513f22887c2c6bef9b796be297d
MD5 c98944de8e98c0fcc8617f36608d17fb
BLAKE2b-256 185060b850cefe8b9df4feef6641e2f32236e9a33895b05f384485e8ffd2e891

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