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 Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

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

djch-0.1.5-py3-none-win_amd64.whl (2.5 MB view details)

Uploaded Python 3Windows x86-64

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

Uploaded Python 3Windows x86

djch-0.1.5-py3-none-musllinux_1_2_x86_64.whl (2.5 MB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

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

Uploaded Python 3musllinux: musl 1.2+ i686

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

Uploaded Python 3musllinux: musl 1.2+ ARMv7l

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

Uploaded Python 3musllinux: musl 1.2+ ARM64

djch-0.1.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.5 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

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

Uploaded Python 3manylinux: glibc 2.17+ s390x

djch-0.1.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl (2.6 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ppc64le

djch-0.1.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl (2.5 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ i686

djch-0.1.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl (2.4 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARMv7l

djch-0.1.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.3 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

djch-0.1.5-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.5-py3-none-win_amd64.whl.

File metadata

  • Download URL: djch-0.1.5-py3-none-win_amd64.whl
  • Upload date:
  • Size: 2.5 MB
  • Tags: Python 3, Windows 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.5-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 729d28d4e91c332bf1d6f541a0f5eb651231517bd9aef07ebf2700d60deaefb7
MD5 845bc8397c1e40b76a54fd8180071b6f
BLAKE2b-256 c3f3056cc46228c13d415b09af73b54888e7d5b913214af44e741a6f8c432f10

See more details on using hashes here.

File details

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

File metadata

  • Download URL: djch-0.1.5-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.5-py3-none-win32.whl
Algorithm Hash digest
SHA256 68554372bb150cc046ebb50a9e725b814960361a03502b4d6477d8c75da70697
MD5 fdd281f32d88d04a87efab804e2cd679
BLAKE2b-256 ba54778033aea1ae4c41dbdc32cf5399741e61aa779612f999452fb5f93902ac

See more details on using hashes here.

File details

Details for the file djch-0.1.5-py3-none-musllinux_1_2_x86_64.whl.

File metadata

  • Download URL: djch-0.1.5-py3-none-musllinux_1_2_x86_64.whl
  • Upload date:
  • Size: 2.5 MB
  • Tags: Python 3, musllinux: musl 1.2+ 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.5-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 7652be9cb5be82321f9fc18b8825455cebcd33f39854eaf13bbdcafc4292d512
MD5 4c2b4e2fab01468450a098f4eadd525b
BLAKE2b-256 78064486ea13bda02104e51aecda52eadbc7096b9b30cfa2014dab84c52f0028

See more details on using hashes here.

File details

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

File metadata

  • Download URL: djch-0.1.5-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.5-py3-none-musllinux_1_2_i686.whl
Algorithm Hash digest
SHA256 6c3409f5ca1d96deff45b5810db63ff21745f2076e49ee6cf4f8bd1b9198363a
MD5 5b65ed08df95947e6b0d77f811b7ed24
BLAKE2b-256 2cbd443bf20d74072caadf378b09812c4f48de430f2afcf758d8ddef90ebed21

See more details on using hashes here.

File details

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

File metadata

  • Download URL: djch-0.1.5-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.5-py3-none-musllinux_1_2_armv7l.whl
Algorithm Hash digest
SHA256 6f2bde0a10ae72b4bfbaaeb815372cf317ebfd5e97f93aaede2d711190f2ac8a
MD5 508aefdbc34cf9b897dd059a3015007b
BLAKE2b-256 c299394a4196b5dd0687e15c06187504a0ce99b6cd2628861738e885af701bac

See more details on using hashes here.

File details

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

File metadata

  • Download URL: djch-0.1.5-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.5-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 558a849187fb664be2579cd98919758c171e2895b0327378c08909aa0e02fd87
MD5 611fe345c61a86b31dd708bb83592d58
BLAKE2b-256 e48a77310b520361373fef4eafe6496751570418bb1db5c8215b880e1f6a4a8f

See more details on using hashes here.

File details

Details for the file djch-0.1.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

  • Download URL: djch-0.1.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
  • Upload date:
  • Size: 2.5 MB
  • Tags: Python 3, manylinux: glibc 2.17+ 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.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 91fd99f72d1c98be2eecc9914ec52854e19d7607d7be1358f400e0cb4bf83ad3
MD5 72a86dd2f73fec2c1a8ebaf2c0953e52
BLAKE2b-256 5afe690a1095b20330a82e2f2fa721fcf2a629e33bbd0bbb771c45a45ce63903

See more details on using hashes here.

File details

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

File metadata

  • Download URL: djch-0.1.5-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.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl
Algorithm Hash digest
SHA256 b313c7b3cfad3d67951505383a38cbbe72fe0f878f34138aa24e288c9e8355f1
MD5 5e7a42f1a3cb0926e0533fdb629ba508
BLAKE2b-256 1b44dc05c9e699aca9ae2207e42f7956a27b7062ded29c833daf50dd252dc38c

See more details on using hashes here.

File details

Details for the file djch-0.1.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl.

File metadata

  • Download URL: djch-0.1.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl
  • Upload date:
  • Size: 2.6 MB
  • Tags: Python 3, manylinux: glibc 2.17+ ppc64le
  • 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.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl
Algorithm Hash digest
SHA256 ed85bf321a4aa4022c6a182dd0ea953edc14b5e440fcd591fae52bcc88988514
MD5 db7c7fb4261adae1667e946c695898e7
BLAKE2b-256 5f51b3a4796062ab8e2135169090096a7e55f5ebf0c082510a714a9c9a3dd9e5

See more details on using hashes here.

File details

Details for the file djch-0.1.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

  • Download URL: djch-0.1.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl
  • Upload date:
  • Size: 2.5 MB
  • Tags: Python 3, manylinux: glibc 2.17+ 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.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 d50731470cf513801e865274e720d76e6f89b18b69f84d0d587e7376f2e2031a
MD5 094dbec6f5c392677cb23403e9787597
BLAKE2b-256 270a9909191227c078ccec4c272d1633d52b5f626413baba5615c2e86dd929a5

See more details on using hashes here.

File details

Details for the file djch-0.1.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl.

File metadata

  • Download URL: djch-0.1.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
  • Upload date:
  • Size: 2.4 MB
  • Tags: Python 3, manylinux: glibc 2.17+ 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.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl
Algorithm Hash digest
SHA256 327174dc1cf9509c886a2753338d6dc4c5d885452a7396aaec5eb6b29a82b50f
MD5 1c18b5a542de187c4c11fcb95bb24420
BLAKE2b-256 7e57ea70632eb7126bea8e08f4fcf88c074b77dbfe28666074ad13cb453508cf

See more details on using hashes here.

File details

Details for the file djch-0.1.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

  • Download URL: djch-0.1.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
  • Upload date:
  • Size: 2.3 MB
  • Tags: Python 3, manylinux: glibc 2.17+ 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.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 3fca4c4a46c883c5906423ac71d923c729683407f9d9785dbb023302dae91fe2
MD5 946f6bbb2d19aa5f0454864ca2eb6f5f
BLAKE2b-256 ca4f2194d2a8d920bedc4ab41d58514e294683991771f4f232bca1fec3496853

See more details on using hashes here.

File details

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

File metadata

  • Download URL: djch-0.1.5-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.5-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 1e09f98dfc062a8658e43fbab7e3f34623f512b54511d041f179bda87aee8bbe
MD5 801cc355892b87fa81ada011e2beed69
BLAKE2b-256 0005657bfbf04881f7163d9c1dc27abf9a6b794782d4edc9ad2b6816e57d6b72

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