{"id":9680,"library":"django-pg-migration-tools","title":"Django PG Migration Tools","description":"django-pg-migration-tools is a Python library that provides a set of specialized Django migration operations designed to make database schema changes safer and more scalable on PostgreSQL. It includes tools for idempotent model creation, concurrent index and constraint management, function migration, and concurrent column alterations, aiming to minimize downtime and avoid common locking issues during deployments. The current version is 0.1.26, and it typically sees releases for new features or bug fixes rather than a fixed cadence.","status":"active","version":"0.1.26","language":"en","source_language":"en","source_url":"https://github.com/kalekseev/django-pg-migration-tools","tags":["Django","PostgreSQL","migrations","database","schema","concurrency"],"install":[{"cmd":"pip install django-pg-migration-tools","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Core framework integration","package":"Django","optional":false},{"reason":"PostgreSQL database adapter","package":"psycopg2-binary","optional":false}],"imports":[{"symbol":"CreateModelIfNotExists","correct":"from django_pg_migration_tools.operations import CreateModelIfNotExists"},{"symbol":"AddIndexConcurrently","correct":"from django_pg_migration_tools.operations import AddIndexConcurrently"},{"symbol":"PostgreSQLCheck","correct":"from django_pg_migration_tools.checks import PostgreSQLCheck"},{"symbol":"MigrateFunction","correct":"from django_pg_migration_tools.operations import MigrateFunction"},{"symbol":"RenameConstraintConcurrently","correct":"from django_pg_migration_tools.operations import RenameConstraintConcurrently"}],"quickstart":{"code":"import os\nfrom django.db import migrations, models\nfrom django_pg_migration_tools.operations import AddIndexConcurrently\n\n# Example of a Django migration using AddIndexConcurrently\n# This migration file would typically be in app_name/migrations/000X_...\n\nclass Migration(migrations.Migration):\n    atomic = False # Required for CONCURRENTLY operations\n\n    dependencies = [\n        ('your_app_name', '0001_initial'), # Replace with your actual dependency\n    ]\n\n    operations = [\n        # Add a B-tree index concurrently on 'field_name' of 'model_name'\n        AddIndexConcurrently(\n            model_name='yourmodelname',\n            name='yourmodelname_field_name_idx',\n            fields=['field_name'],\n            db_tablespace='', # Optional: specify tablespace\n            opclasses=[],     # Optional: specify opclasses for the index\n            condition=None    # Optional: specify a partial index condition\n        ),\n        # Other operations could follow\n    ]\n","lang":"python","description":"This quickstart demonstrates how to use `AddIndexConcurrently` within a Django migration. Note that for any 'CONCURRENTLY' operation, the migration must set `atomic = False`. This example assumes you have a Django project and an app named `your_app_name` with a model `yourmodelname` and a `field_name`."},"warnings":[{"fix":"Add `atomic = False` as a class attribute to your `migrations.Migration` class: `class Migration(migrations.Migration): atomic = False`.","message":"Operations ending in 'Concurrently' (e.g., `AddIndexConcurrently`) require careful consideration. They leverage specific PostgreSQL features that prevent them from running within a transaction block. You must set `atomic = False` in your `Migration` class for these to work, which means the entire migration is not atomic.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Add `'django_pg_migration_tools.checks.PostgreSQLCheck'` to your Django `settings.py` `SILENCED_SYSTEM_CHECKS` or explicitly run it during development/testing. Ensure you understand its warnings.","message":"Ignoring the `PostgreSQLCheck` can lead to issues. The library provides `django_pg_migration_tools.checks.PostgreSQLCheck` to identify and warn about common footguns when using its operations, especially regarding concurrent operations being blocked by active transactions. It is recommended to include this check.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Consult the project's GitHub releases page or `pyproject.toml`/`setup.py` for specific changes before upgrading. Pin your dependency versions to prevent automatic updates.","message":"As a pre-1.0 library (currently 0.1.x), the API surface can change between minor versions (e.g., 0.1.25 to 0.1.26) without explicit deprecation warnings or major announcements for less commonly used features. Always review the GitHub changelog or release notes before upgrading to avoid unexpected breaks.","severity":"breaking","affected_versions":"< 1.0.0"}],"env_vars":null,"last_verified":"2026-04-17T00:00:00.000Z","next_check":"2026-07-16T00:00:00.000Z","problems":[{"fix":"Use `CreateModelIfNotExists` from `django_pg_migration_tools.operations` instead of `migrations.CreateModel`. This operation creates the table only if it doesn't already exist, making the migration idempotent.","cause":"A previous migration failed after creating the table but before marking the migration as applied, or the table was created manually. When `CreateModel` runs again, it errors.","error":"psycopg2.errors.DuplicateTable: relation \"appname_modelname\" already exists"},{"fix":"For any migration file containing `Concurrently` operations, set `atomic = False` as a class attribute within the `Migration` class: `class Migration(migrations.Migration): atomic = False`.","cause":"PostgreSQL's `CREATE INDEX CONCURRENTLY` (and other concurrent DDL operations) cannot run inside a transaction block. Django migrations, by default, run inside a transaction.","error":"django.db.utils.OperationalError: cannot CREATE INDEX CONCURRENTLY in a transaction (or similar for other CONCURRENTLY operations)"},{"fix":"Consider using `RemoveConstraintConcurrently` or `RenameConstraintConcurrently` from `django_pg_migration_tools.operations`. These operations often include `if_exists=True` or handle the existence check internally, making them more robust for idempotent migrations. Ensure you are using the correct constraint name.","cause":"Attempting to remove a constraint using `migrations.RemoveConstraint` that might already be removed, or attempting to rename it with `RenameConstraintConcurrently` when the old name is gone.","error":"django.db.utils.ProgrammingError: constraint \"your_app_tablename_field_check\" does not exist"}]}