Breaking change
The wheel no longer installs docs, examples and dts_test_project as top-level packages (#1224, thanks @Ramblurr). Every release up to 3.10.2 shipped 159 stray files into site-packages as importable top-level names — including dts_test_project, which could shadow a project's own module. If you were importing any of them, accidentally or otherwise, that will now fail. They remain in the sdist.
Fixes
Cloning a tenant works again when a model has ever been renamed (#1236). clone_schema failed with relation "<dest>.<old table name>_id_seq" does not exist on any schema containing a renamed table that holds data. Postgres derives a sequence's name from its table when the column is created and never revisits it, so ALTER TABLE ... RENAME — any Django RenameModel — leaves the sequence under the table's old name, while the destination's sequences are created fresh by CREATE TABLE ... (LIKE ... INCLUDING ALL) and named after the current table. The two diverge for every renamed table. Identity sequences are now found through pg_depend and the destination's own sequence resolved with pg_get_serial_sequence.
clone_schema can be called inside transaction.atomic() (#1150, thanks @Winnie-Fred). It called transaction.commit(), which raises TransactionManagementError inside an atomic block — so cloning from the Django admin or a TestCase was impossible. Fixes #1155 and #694.
Cloning no longer fails silently when TENANT_BASE_SCHEMA does not exist (#1133, thanks @hassaanalansary) — it falls back to creating the schema, which matters on a first run against an empty database.
New
A subprocess migration executor (#1228, thanks @tjwalch). migrate_schemas normally runs every tenant in one long-lived process, and per-tenant state is never released, so resident memory climbs until the OOM killer reaps the process mid-migration. --executor=subprocess spawns a fresh manage.py migrate_schemas --schema <name> per tenant so memory returns to baseline between them, with --parallel N / TENANT_SUBPROCESS_PARALLEL for concurrency. Opt-in; existing behaviour is unchanged. Fixes #1211.
A mypy plugin (#1231, thanks @bvedad). plugins = ["django_tenants.mypy_plugin"] declares request.tenant on HttpRequest, so strict mypy with django-stubs >= 6.0 stops reporting attr-defined on every access. Subclasses, including DRF's Request, inherit it.
Packaging and CI
Releases now publish to PyPI automatically via a release.yml workflow using trusted publishing (OIDC) — no API token in the repository. The build refuses to publish when the git tag disagrees with the version in pyproject.toml.
Docs
literalinclude for the settings examples so they can't drift from tested code (#1146, thanks @foarsitter); the tenant-schema context manager is now documented (#1215, thanks @fairwiz); grammar in the Admin Support section (#1234, thanks @NaftaliHolland).
A note on the version number
This follows 3.10.2, the previous PyPI release. There is an earlier v3.11.2 tag in this repository, but it points at the "Bump version to 3.10.2" commit, contains 3.10.2, and was never published to PyPI. Numbering this release in the 3.11.x range would have sorted below that tag, so it goes out as 3.12.0.
Full Changelog: v3.10.0...v3.12.0