All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
No notable unreleased changes
0.1.26 - 2026-02-27
- Updated supported versions of Python, Django and Postgres as follows:
- Added support for Python 3.13 and 3.14
- Dropped support for Django 4.2 and 5.0 and added support for Django 5.2 and 6.0
- Dropped support for Postgre 13 and added support for Postgres 17 and 18
- Fixed a bug where a unique constraint with
nulls_distinct=Falsewas created without "NULLS NOT DISTINCT" bySaferAddUniqueConstraint. - Fixed a bug where a unique constraint with
nulls_distinct=Falseresulted in invalid SQL whenSaferAddUniqueConstraintwas used with PostgreSQL 14.x or earlier (which does not support "NULLS NOT DISTINCT"). The operation will now raise aConstraintNotSupportedexception instead.
0.1.25 - 2026-02-24
- Updated the "make constraint" queries to be schema-aware (for multi-tenancy).
0.1.24 - 2025-12-12
- Fixed a bug where
SaferRemoveFieldForeignKeyrelied on the Foreign Key also existing in Django state, even when being performed as a database only operation. The name and model are already provided as part of the operation.
0.1.23 - 2025-11-18
- Fixed a bug where
SaferAddFieldForeignKeyignored theForeignKeyto_fieldparameter, resulting in an incorrect column type and incorrect primary key reference.
0.1.22 - 2025-08-07
- Fixed a bug where using
SaferRemoveFieldForeignKeyon a field that had null=False was raising an error. This shouldn't be the case as the nullability of the field is not important when removing the FK field.
0.1.21 - 2025-07-07
- Fixed a bug where
SaferAddUniqueConstraintandSaferRemoveUniqueConstraintwould accept unique constraints with expressions on them, but produce invalid SQL. This is now handled in the same way as unique constraints with conditions, as unique indexes, as per the equivalent DjangoAddConstraintandRemoveConstraintoperations.
0.1.20 - 2025-06-19
SaferRemoveUniqueConstraintnow produces the best-effort plan for a migration that would be performed if the constraint wanting to be removed does in fact exist when runningsqlmigrate. Previously, this would act as if the constraint did not exist is usingsqlmigrate.SaferAddUniqueConstraintnow produces a backwards plan as if the constraint has already been created when runningsqlmigrate.
0.1.19 - 2025-04-04
SaferAddFieldForeignKeydid not work correctly when the primary key field on the referred table had a column name that different from the field name.
0.1.18 - 2025-02-11
SaferAlterFieldSetNotNulldid not work when the field dropping the NOT NULL constraint was a ForeignKey. An error such as:django.db.utils.ProgrammingError: column "foo" does not existwould have been raised instead.
0.1.17 - 2025-01-14
- Enhanced
SaferAddUniqueConstraintto support aUniqueConstraintwith thedeferrableargument. - A new operation to remove a foreign key field:
SaferRemoveFieldForeignKey. - A new operation
SaferRemoveCheckConstraintwhich does the opposite ofSaferAddCheckConstraint.
0.1.16 - 2025-01-08
- A new type of index
UniqueIndexthat also accepts conditions. - A new operation to add one-to-one fields in a safer way:
SaferAddFieldOneToOne.
0.1.15 - 2024-12-19
- The
migrate_with_timeoutscommand now acquires a session-level advisory lock before it begins. This helps preventing concurrent instances of the migrate command from running at the same time, which could cause unexpected crashes.
0.1.14 - 2024-12-17
- A new operation to add a check constraint to an existing table has been
added:
SaferAddCheckConstraint.
- Introspection queries are now skipped when running
sqlmigrate. The result of thesqlmigratecommand will instead show a best-effort plan to perform the migration without knowing the current state of the database. When runningmigrate, however, the introspection queries will find the most adequate plan; taking into consideration idempotency and reentrancy.
0.1.13 - 2024-12-11
- Use
schema_editorinstead of opening a new cursor for executing DDL queries. This is so thatsqlmigratecan show the queries without actually running them.
0.1.12 - 2024-12-10
- Fixed a bug preventing the
SaferAddFieldForeignKeyoperation of being initialised with atoargument that is a string instead of models.Model
0.1.11 - 2024-12-06
- A new operation to add foreign key fields to an existing table has been
added:
SaferAddFieldForeignKey
0.1.10 - 2024-11-22
- The
migrate_with_timeoutscallback argumentRetryStatenow includes the name of the database that the retry being performed is associated with.
- Fixed an import error introduced on v0.1.9 that would break that version for users of psycopg2.
0.1.9 - 2024-11-14
- The
SaferAlterFieldSetNotNulloperation was added. This will more safely change a field from nullable to not nullable.
0.1.8 - 2024-10-31
- Fixed a bug where
SaferAddUniqueConstraintandSaferRemoveUniqueConstraintwould accept unique constraints with conditions on them, but produce invalid SQL. Unique constraints with conditions are now handled as partial unique indexes, as per the equivalent DjangoAddConstraintandRemoveConstraintoperations.
SaferRemoveUniqueConstraintoperation was added. This is the complement forSaferAddUniqueConstraint- but with the forward and backwards operations swapped.
0.1.7 - 2024-10-09
- Fixes a bug in
migrate_with_timeoutswhere passing astdoutparameter to the command would raise a TypeError.
0.1.6 - 2024-10-08
migrate_with_timeoutscallbacks can now access the migration stdout viaRetryState.stdoutand the time since the migration started withRetryState.time_since_start.- Added a new operation
SaferAddUniqueConstraintthat provides a way to safely create unique constraints.
0.1.5 - 2024-10-07
migrate_with_timeoutswill now raiseMaximumRetriesReachedinstead ofCommandErrorwhen the maximum number of retries is reached.
0.1.4 - 2024-10-01
- The implementation of
migrate_with_timeoutsnow has additional flags to perform retries:./manage.py migrate_with_timeouts \ --lock-timeout-in-ms=10000 \ --lock-timeout-max-retries=3 \ --lock-timeout-retry-exp=2 \ --lock-timeout-retry-min-wait-in-ms=3000 \ --lock-timeout-retry-max-wait-in-ms=10000 \ --statement-timeout-in-ms=10000 \ --retry-callback-path="dotted.path.to.callback.function"
0.1.3 - 2024-09-03
SaferRemoveIndexConcurrentlymigration operation to drop Postgres indexes in a safer way than Django'sRemoveIndexConcurrently.
- The internal implementation for
SaferAddIndexConcurrentlyhas been changed to inherit from Django'sAddIndexConcurrentlyoperation rather than Django'sOperationclass. This means that the interface is now the same and the "hints" argument is not valid any longer.
0.1.2 - 2024-08-23
- Fixes a bug where the
SaferAddIndexConcurrentlyclass would try to perform a migration regardless of whether the router would allow it (throughrouter.allow_migrate).
0.1.1 - 2024-08-14
- Non-functional changes for the documentation to be properly linked in PyPI.
0.1.0 - 2024-08-12
apply_timeoutscontext manager for controlling the values of lock_timeout and statement_timeout.migrate_with_timeoutsmanagement command that applies lock_timeout and statement_timeout to Django's migrate command.SaferAddIndexConcurrentlymigration operation to create new Postgres indexes in a safer, idempotent way.