Open-source project
adamchainz/django-mysql avatar
adamchainz/django-mysql

Django-MySQL: the MySQL and MariaDB features Django leaves out

:dolphin: :horse: Extensions to Django for use with MySQL/MariaDB

586 stars115 forksPythonMIT

At a glance

What is it?
Django-MySQL is a thin layer that adds the parts of MySQL and MariaDB that the built-in backend does not expose, from dynamic columns and upsert based caching to named locks and a table lock manager for awkward migrations.
Who is it for?
Django-MySQL is the right tool for a Django project that has already chosen MySQL or MariaDB and keeps hitting the places where the built-in backend stops short, particularly around MariaDB specific data structures, caching that benefits from the server upsert statement, and migrations that need a table lock.
Can I use it commercially?
Yes. MIT 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 8 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

A layer over Django's built-in MySQL backend

Django ships with its own MySQL and MariaDB support, and the gap this project fills is the features of those two databases that the generic Django ORM cannot express. The project describes itself as extending Django's built-in MySQL and MariaDB support with their specific features that are not available on other databases.

That framing matters, because it tells you what the library is and is not. It is not a database driver, and it is not an alternative ORM. It is a set of extensions that sit alongside the standard backend, and everything it offers is MySQL or MariaDB specific by definition.

The audience is therefore narrow and well defined: a Django project that has already committed to MySQL or MariaDB and keeps running into the places where the standard backend stops. A team that has not chosen a database engine yet has no reason to be here.

The project is maintained by Adam Johnson, published on PyPI as django-mysql, MIT licensed, and documented on Read the Docs. The readme points at an exposition page that lists every feature, and the requirement line is short: Django 5.2 or newer, on Python 3.11 or newer. The package metadata declares support for Django 5.2, 6.0 and 6.1 and for Python 3.11 through 3.15, and it is marked as typed, which matters for a library that sits between your models and your database.

The repository layout is a conventional modern Python project, with a src directory, a tests directory, a docs directory, a tox.ini, a uv.lock file, a pre-commit configuration and a HISTORY.rst changelog.

QuerySet extensions for iteration, counting and explaining

The first group of features is the QuerySet layer, and these are the ones a Django developer will reach for first.

Smart iteration is chunked pagination across a large queryset. The normal Django behaviour is to fetch the whole result set into memory and then page through it, which is fine until the queryset is large enough that the memory or the query cost becomes a problem. The chunked approach walks the result set in pieces, so the memory profile changes and the cost of the work becomes proportional to what you actually consume.

approx_count gives quick estimates of count(). A count() over a large table is a full scan, and when the exact number is not needed, an estimate that the server can answer cheaply is the better trade. The naming is deliberate and so is the contract: it is an estimate, and the documentation is where you check what it returns.

Query hints are the third feature, and the mechanism is the database's own facility for steering the optimizer rather than overriding its choices in the query text.

The fourth is the quickest way to see the SQL behind a queryset, with a quick pt-visual-explain of the underlying query. The tool is the MySQL utility that turns an EXPLAIN into a tree, and wiring that into the shell is a small thing that saves a lot of time when a query plan is not what you expected.

Taken together, these four are a coherent set. They are all about the fact that a big table in MySQL behaves differently from a big table in SQLite, and a Django project that has grown into that territory needs the escape hatches.

Model fields for data shapes MySQL supports

The second group is model fields, and these exist because MySQL and MariaDB have column types that Django's field set does not model.

MariaDB Dynamic Columns are for storing dictionaries, and the mechanism is a MariaDB feature that lets a row carry a set of name and value pairs in one column. A Django model field for it means you can store semi-structured data per row without dropping to raw SQL or serialising it into a text column.

Comma-separated fields are for storing lists and sets, in a column that holds the values separated by commas. The project describes these plainly, and the trade-off is the familiar one: the storage is compact and readable in the shell, and the querying is not something the relational algebra can help with.

The 'Missing' fields are the third item, and the name refers to the field types that MySQL has and Django does not. They are differently sized BinaryField and TextField classes, which is a way of saying that a binary or text column can be declared with a specific length rather than with the generic type Django offers.

The last of them is BooleanFields represented by BIT(1), which is the MySQL way of storing a boolean in a single bit rather than in the TINYINT that Django's BooleanField uses. A model field that maps to BIT(1) produces a schema that matches what the rest of the MySQL ecosystem expects, which matters if other tools read the table.

The general pattern is the same across all four. The database has a capability, Django's ORM cannot express it, and this library provides the field that maps onto it rather than making you fall back to raw SQL.

Beyond the ORM: caching, locks and status inspection

The last group of features is the part that makes this library more than a set of field types, and it is where the operational value is.

There is a new cache backend that makes use of MySQL's upsert statement and does compression. The upsert is the interesting half, because a cache write that has to check for existence and then update or insert is two round trips, and the server can do both in one statement. The compression is the other half, and it is a cache that stores less than the raw value.

Named locks are for easy locking of external resources. MySQL has advisory named locks, and this exposes them as something a Django application can take and release, which is the primitive you need to stop two workers doing the same external work at the same time.

The table lock manager is described as being for hard to pull off data migrations, and that phrase is doing a lot of work. A migration that has to rewrite a large table can be blocked by a running query, and the usual outcome is a migration that hangs for a long time. Taking a table lock at the right moment turns that into a controlled pause.

Finally there is status variable inspection with utility methods, which is the server's own runtime counters exposed to your code, and a group of ORM expressions for over 20 MySQL specific functions, which is the escape hatch for a query function that Django has no expression for.

This is the set of features that decides adoption. A project that only wants dynamic columns can take a small part of the library, while a project that wants the cache backend and the named locks is taking on the whole operational surface.

Installing it: the packaging metadata tells you what fits

The readme points at the installation page of the documentation rather than repeating the steps, so the useful part of the installation story is the metadata in the project file.

The package is named django-mysql and the current version is 4.19.0, the licence is MIT, and the author is Adam Johnson.

toml
[project]
name = "django-mysql"
version = "4.19.0"
description = """\
  Django-MySQL extends Django's built-in MySQL and MariaDB support their specific features not available on other \
  databases.\
  """
readme = "README.rst"
keywords = [
  "Django",
  "MariaDB",
  "MySQL",
]
license = "MIT"
license-files = [ "LICENSE" ]

The runtime requirement is a single dependency.

toml
dependencies = [
  "django>=5.2",
]

So the library itself pulls in nothing beyond Django, and the MySQL driver is not its dependency. That is deliberate, since the driver choice, mysqlclient or another, is a deployment decision that depends on your platform and your build.

The test group shows what the maintainer needs to run the suite, and it is a useful list for anyone building a similar environment, since a MySQL backend cannot be tested against a mock.

toml
test = [
  "coverage[toml]",
  "django-jsonfield-backport",
  "mariadb-dyncol",
  "mysqlclient>=1.4.3",
  "pytest>=9",
  "pytest-django",
  "pytest-flake8-path",
  "pytest-randomly",
  "typing-extensions",
  "unittest-parametrize>=1.8",
]

The mariadb-dyncol package in that list is the MariaDB dynamic columns extension. The build backend is uv_build, the project is built with uv, and the repository carries a uv.lock file, a tox.ini for running the suite across versions, and a pre-commit configuration.

Where Django-MySQL is the wrong choice

Four limits, and the first one is the price of admission.

The library binds you to the database engine. Every feature it offers is MySQL or MariaDB specific, which is the point, and it means a project that might move to PostgreSQL later is accumulating work it will have to undo. The dynamic columns, the BIT(1) booleans, the named locks, the table lock manager, the upsert cache backend, none of them have a portable equivalent in the library.

The second is that several of the features change semantics rather than adding to them. approx_count returns an estimate where count() returns an exact number, and a report built on an estimate is a different report. Smart iteration changes how a queryset is consumed, and chunked pagination over a large table is not the same operation as a limit and offset, particularly when rows are being modified during the walk. These are good tools, but they are tools that change the contract, and the documentation is where you check what you are agreeing to.

The third is the table lock manager, which is a blunt instrument by nature. A lock that is designed to pause queries so a migration can proceed is exactly the kind of thing that turns a slow migration into an outage when it is taken at the wrong moment, and the library cannot decide that for you.

The fourth is the version floor. The package requires Django 5.2 or newer and Python 3.11 or newer, and the classifiers name Django 6.1 and Python 3.15, so the project tracks new releases. A team on an older Django cannot use the current version of the library at all, which is worth checking before anything else.

Maintenance signals and the MIT licence

The project is not archived, and the last push to the default branch was on 2026-10-15. There are no GitHub releases, but the version in the packaging metadata is 4.19.0, and the changelog lives in HISTORY.rst and in the documentation, with a urls.Changelog entry pointing at it. The packaging metadata also carries urls.Funding pointing at the maintainer's books, which is the sort of thing that appears in a project that has a maintainer with a long runway.

The badges in the readme are worth reading as signals rather than as decoration. There is a coverage badge showing 100 percent, a code style badge for black, a pre-commit enabled badge, a read the docs badge, and a build status badge for the GitHub Actions workflow. A library claiming full coverage of a codebase that integrates with a database is a claim worth taking seriously, because the only way to reach it is to run against real servers in the test matrix.

The licence is MIT, and the packaging metadata declares it in the project table and points at a LICENSE file with the license-files key, so the licence text is shipped with the distribution rather than only being a line in the readme. That is a permissive grant, so you can use it inside a commercial product and modify it, provided you keep the notice, and this is not legal advice.

For an adopter, the practical summary is short. The library is small, well typed, well covered and maintained, it asks for a recent Django and Python, and it gives you the parts of MySQL that Django has never grown. What it asks in return is that you accept the engine-specific consequences.

Editorial conclusion

Django-MySQL is the right tool for a Django project that has already chosen MySQL or MariaDB and keeps hitting the places where the built-in backend stops short, particularly around MariaDB specific data structures, caching that benefits from the server upsert statement, and migrations that need a table lock. It is the wrong choice for a project that might change database engine, because the value of the library is exactly the part that is not portable, and it is the wrong choice for a team that has not yet decided whether it needs those features at all. Before adopting, read the exposition page that lists everything the library adds, check that your Django version is covered by the classifiers, and confirm that the features you want are opt-in rather than changes to the default behaviour.

Frequently asked questions

What does django-mysql add to Django's MySQL support?

It extends Django's built-in MySQL and MariaDB support with the features those databases have and other databases do not: QuerySet extensions, model fields such as MariaDB dynamic columns and BIT(1) booleans, ORM expressions for over 20 MySQL specific functions, an upsert based cache backend, named locks, a table lock manager and status variable inspection.

What are the requirements for installing django-mysql?

The runtime dependency is Django 5.2 or newer, and the project requires Python 3.11 or newer. The classifiers list Django 5.2, 6.0 and 6.1 with Python 3.11 through 3.15, and the package is MIT licensed with the author listed as Adam Johnson.

What do smart iteration and approx_count do in django-mysql?

Smart iteration is chunked pagination across a large queryset, and approx_count gives quick estimates of count() rather than the exact number. Both are listed among the QuerySet extensions, along with query hints and a quick pt-visual-explain of the underlying query.

Does django-mysql provide a cache backend and locking tools?

Yes. The package includes a new cache backend that makes use of MySQL's upsert statement and does compression, named locks for easy locking of external resources, and a table lock manager aimed at data migrations that are hard to pull off.

Official sources

  1. adamchainz/django-mysql on GitHub
  2. Issues
  3. License: MIT
  4. Project website
  5. README
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/adamchainz-django-mysql.svg)](https://hysenlabs.com/projects/adamchainz-django-mysql)

Was this useful?