Library / SDK
django-commons/django-prometheus avatar
django-commons/django-prometheus

django-prometheus: latency histograms where the bucket defaults are the decision

Export Django monitoring metrics for Prometheus.io

1,674 stars264 forksPythonApache-2.0

At a glance

What is it?
django-prometheus exports request, ORM and cache metrics through middleware and backend configuration; model-operation metrics require a model mixin. Its seventeen default latency buckets run to 75 seconds, which is where you will want to change something, and the README says custom buckets cost performance.
Who is it for?
Adopt django-prometheus if you run Django and want framework-level metrics without writing instrumentation, because swapping three configuration points, the middleware pair, the database `ENGINE` and the cache `BACKEND`, gets you request latency, ORM query counts, cache hit rates and per-model write counters in one sitting.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 22 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three configuration swaps and nothing else

The integration surface is unusually small, which is the reason the library is easy to adopt and easy to drop.

Installation is one line, and it pulls in the client library:

shell
pip install django-prometheus

If you are working from a clone, the README gives the local alternative, `python -m pip install path-to-where-you-cloned-django-prometheus`.

Then three changes. Add `django_prometheus` to `INSTALLED_APPS`. Wrap your middleware list with the two middleware classes, before and after everything else:

python
MIDDLEWARE = [
    'django_prometheus.middleware.PrometheusBeforeMiddleware',
    # All your other middlewares go here, including the default
    # middlewares like SessionMiddleware, CommonMiddleware,
    # CsrfViewmiddleware, SecurityMiddleware, etc.
    'django_prometheus.middleware.PrometheusAfterMiddleware',
]

The comment in the README is the instruction that matters. All other middleware, including Django's defaults, goes between the two, which is what lets the pair measure the whole request rather than a slice of it.

Finally, mount the metrics view in `urls.py`:

python
urlpatterns = [
    ...
    path('', include('django_prometheus.urls')),
]

Mounting at the root path is the documented example, and it is the one place worth deviating from. The README does not describe authentication on that view, so if you mount it at `/` you have put an unauthenticated endpoint for internal metrics at the root of your site. Mounting it under a path your reverse proxy restricts is the smaller change.

If you cannot touch the codebase at all, the README describes a settings-only path that injects the middlewares and the app without code changes, for deployments where the settings file is the only lever.

Latency buckets are a configuration choice, not a default to accept

Prometheus groups latency observations into histogram buckets, and django-prometheus lets you replace the bucket boundaries. The default tuple is:

python
PROMETHEUS_LATENCY_BUCKETS = (
    0.01,
    0.025,
    0.05,
    0.075,
    0.1,
    0.25,
    0.5,
    0.75,
    1.0,
    2.5,
    5.0,
    7.5,
    10.0,
    25.0,
    50.0,
    75.0,
    float("inf"),
)

Read the shape of that list. It resolves a millisecond bucket finely, a one-second bucket reasonably, and then walks in very wide steps out to seventy-five seconds. That is a distribution shaped for short web requests where the interesting question is whether a call was under 100 milliseconds, with a long tail to catch timeouts.

The README is direct about the cost of changing it: you can define custom buckets for latency, and adding more buckets decreases performance but increases accuracy. That is the real trade-off, and it is why the bucket tuple is the first thing to tune on a project whose latency profile differs from the assumption.

The alternative tuple the README offers for reference runs from 0.1 to 30.0 with eighteen boundaries and its own `float("inf")`, so it is coarser at the low end and stops earlier at the high end.

One nuance worth knowing: the latency setting governs latencies, and other metric families may have their own bucket handling. The detailed per-metric reference is in `documentation/metrics.md`, which the README points to rather than duplicating.

Swapping the database and cache backends

The ORM and cache metrics are opt-in by substitution rather than by configuration flag, and the mechanism is a thin wrapper backend.

For databases, the README says SQLite, MySQL and PostgreSQL can be monitored, and you replace the `ENGINE` property, replacing `django.db.backends` with `django_prometheus.db.backends`:

python
DATABASES = {
    "default": {
        "ENGINE": "django_prometheus.db.backends.sqlite3",
        "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
    },
}

For caches, the same idea with `django.core.cache.backends` replaced by `django_prometheus.cache.backends`. The README lists filebased, memcached and redis caches as supported, and the example configures a filebased cache:

python
CACHES = {
    "default": {
        "BACKEND": "django_prometheus.cache.backends.filebased.FileBasedCache",
        "LOCATION": "/var/tmp/django_cache",
    }
}

Two practical notes about the swap. First, the backend you name must exist in that namespace, so a cache library you use today has a matching wrapper or it does not get metrics. The development `requirements.txt` shows the breadth tested, with `django-redis`, `pylibmc`, `pymemcache` and `python-memcached` alongside `psycopg` and `mysqlclient`.

Second, the wrapper sits between your code and the real backend, so it is a place a bug can live. It is also the reason the change is easy to revert: restoring the original dotted path removes the metrics and nothing else.

Model mixins count writes, not rows

Monitoring per-model activity is done by adding a mixin, and the README is careful to state what the numbers mean.

You inherit from `ExportModelOperationsMixin` with a model name:

python
from django_prometheus.models import ExportModelOperationsMixin


class Dog(ExportModelOperationsMixin("dog"), models.Model):
    name = models.CharField(max_length=100, unique=True)
    breed = models.CharField(max_length=100, blank=True, null=True)
    age = models.PositiveIntegerField(blank=True, null=True)

The string argument is the value of the `model` label, and three metrics result: `django_model_inserts_total{model="dog"}`, `django_model_updates_total{model="dog"}` and `django_model_deletes_total{model="dog"}`.

Two details keep people from building the wrong dashboard. The mixin is safe to add to existing models because it does not require a migration. And the metrics are counters of creations, modifications and deletions done in the current process, not gauges of the number of objects in the model. A counter you never reset does not tell you how many dogs exist; it tells you how many inserts have happened since the process started.

Migrations are monitored separately, starting with Django 1.7, through two gauges, `django_migrations_applied_by_connection` and `django_migrations_unapplied_by_connection`. The README suggests alerting when there are unapplied migrations, which is a reasonable use of a gauge, and the whole group can be turned off with `PROMETHEUS_EXPORT_MIGRATIONS` set to False.

The naming follows the `django_model_` prefix with the model name as a label rather than in the metric name, which means adding a new model creates a new label value rather than a new time series family. That is the right shape for cardinality, and it is also why the `model` argument matters.

Metric namespace and your own application metrics

Two extensions are documented, and they cover the two situations most projects hit.

A namespace prefixes every metric this library exports, which is what you want if you run two Django applications behind one Prometheus:

python
PROMETHEUS_METRIC_NAMESPACE = "project"

The effect is visible in the exposition format, where every metric gains the prefix:

text
project_django_http_requests_total_by_method_total{method="GET"} 1.0

For application-level metrics, the answer is not this library at all. You use `prometheus_client` directly, and the README notes the exporter is global and will pick up your metrics. Since the client library is already a dependency, that is zero additional work.

For metrics about Django internals themselves, the README asks you to extend django-prometheus' classes, and it asks contributors to send pull requests. It also points at two Prometheus documents as the style reference, one on instrumentation and one on naming, which is the correct guidance because the cost of a badly named metric is permanent while the cost of a badly bucketed one is only storage.

Scrape configuration is left as an example rather than a recommendation. The repository contains an `examples/prometheus/` directory with a `prometheus.conf` that scrapes `127.0.0.1:8001`, which tells you the port the endpoint is expected on but is otherwise a starting point.

Support, versioning, and where it stops

The packaging metadata tells you the compatibility envelope precisely, which is more than the README's requirements line does.

The README states the requirements as Django 4.2 or later and Python 3.10 and above. `setup.py` narrows that: `python_requires` is `>=3.10` and `install_requires` is `Django>=4.2,<6.2,!=5.0.*` plus `prometheus-client>=0.7`. The exclusion of all 5.0 releases is the interesting part, and it is not something the prose mentions. Anyone running a Django 5.0 deployment should know the library declares it unsupported before spending time on it.

The version number is read out of the package source at build time rather than duplicated. `setup.py` opens `django_prometheus/__init__.py`, matches a `__version__ = '...'` line with a regular expression, and raises a `RuntimeError` if it is not found. So there is one place to bump a version, which is why `update_version_from_git.py` sits at the repository root.

Other structural facts: the test configuration in `pyproject.toml` adds `--ignore django_prometheus/tests/end2end` to pytest's default arguments, so end-to-end tests are present and excluded by default. Linting is configured through `ruff.toml` and `.pre-commit-config.yaml`. The license is Apache-2.0, with `CHANGELOG.md`, `CONTRIBUTING.md` and `CODE_OF_CONDUCT.md` at the root.

The boundary of the tool is the important limitation. It measures requests, ORM operations, cache operations, model writes and migration state. It does not report row counts, does not trace a request across services, does not alert, and does not tell you which of your hundreds of querysets is slow beyond giving you counts per label. For attribution work you need something else. What django-prometheus gives you is a complete, correctly labelled baseline of framework behaviour, and gaps inside your own code have to be instrumented with `prometheus_client` by hand.

Editorial conclusion

Adopt django-prometheus if you run Django and want framework-level metrics without writing instrumentation, because swapping three configuration points, the middleware pair, the database `ENGINE` and the cache `BACKEND`, gets you request latency, ORM query counts, cache hit rates and per-model write counters in one sitting. Do not adopt it expecting table-level insight or object counts: the model mixin exports counters of writes performed in the current process, and the README states plainly that they are not gauges of how many objects exist, so a dashboard built on it will mislead anyone who expects row counts. Verify three things before enabling it: that your latency distribution fits the default `PROMETHEUS_LATENCY_BUCKETS`, which starts at 0.01 and steps through seventeen boundaries ending at `float("inf")` and is tuned for a very different latency profile than most Django applications; that the endpooint is not reachable from outside your monitoring network, since the metrics view is mounted with `path('', include('django_prometheus.urls'))` and the README does not describe authentication on it; and that your `prometheus-client` version is compatible, since `install_requires` pins `prometheus-client>=0.7` while the development `requirements.txt` asks for `>=0.26.0`. The license is Apache-2.0, v2.5.0 shipped on 2026-05-26, and the last push was on 2026-10-01.

Frequently asked questions

How do I install django-prometheus and enable it?

Run `pip install django-prometheus`, add 'django_prometheus' to INSTALLED_APPS, wrap your MIDDLEWARE list with PrometheusBeforeMiddleware and PrometheusAfterMiddleware so all other middleware sits between them, and mount `path('', include('django_prometheus.urls'))` in urls.py.

How do I monitor database and cache operations in django-prometheus?

By substitution. Replace django.db.backends with django_prometheus.db.backends in your database ENGINE, and django.core.cache.backends with django_prometheus.cache.backends in your cache BACKEND. SQLite, MySQL, PostgreSQL, filebased, memcached and redis are listed as supported.

What versions of Django and Python does django-prometheus support?

The README states Django 4.2 or later and Python 3.10 and above. setup.py is more precise, with install_requires set to Django>=4.2,<6.2,!=5.0.* and python_requires >=3.10.

Do the django-prometheus model metrics report row counts?

No. The README states the exported metrics are counters of creations, modifications and deletions done in the current process, and that they are not gauges of the number of objects in the model.

Can I change the latency buckets in django-prometheus?

Yes, by setting PROMETHEUS_LATENCY_BUCKETS to your own tuple. The README warns that adding more buckets decreases performance but increases accuracy, and the default tuple spans seventeen boundaries from 0.01 up to float("inf").

How do I add my own metrics to a django-prometheus setup?

Use prometheus_client directly for application-level metrics, since the exporter is global and picks them up. For metrics about Django internals, extend django-prometheus' classes and follow the Prometheus instrumentation and naming practices the README links.

Official sources

  1. django-commons/django-prometheus on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/django-commons-django-prometheus.svg)](https://hysenlabs.com/projects/django-commons-django-prometheus)

Was this useful?