Migrations in Django

medium · Django

he Version Control System for Databases In Django’s architecture, Migrations function exactly like version control (such as Git) but are built specifically for your database schemas. Instead of requiring you to write manual, risk-prone SQL statements to alter tables when your code changes, Django tracks changes to your models and generates incremental execution scripts automatically. This ensures your database layout stays perfectly in sync with your Python models across all environments—from local development setups to multi-node production clusters. The Two-Step Database Lifecycle Workflow Modifying a database structure is split into two distinct steps to decouple identifying changes from executing them . [Edit models.py] ──► python manage.py makemigrations ──► Generates Migration File (.py) │ [Updated Database] ◄── python manage.py migrate ◄────────────────┘ 1. Compilation Stage ( makemigrations ) The makemigrations command scans your active models.py files, cross-references them against your existing migration files, and calculates the structural difference (the delta). It then writes a new Python script containing the instructions to apply those changes. Bash python manage.py makemigrations Note: This stage is completely non-destructive. Running makemigrations only builds new configuration scripts inside your app’s migrations/ folder; it does not touch your live database tables. 2. Execution Stage ( migrate ) The migrate command reads any pending migration scripts and executes them against your database. It checks a specialized internal table named django_migrations to see which scripts have already run, ensuring it only applies new, unexecuted migrations. Bash python manage.py migrate Anatomy of a Migration File Migration files are written in pure Python, not raw SQL. This abstraction allows the same migration script to run seamlessly across completely different database systems (such as switching from a local SQLite setup to production PostgreSQL). Here is a look inside a typical auto-generated migration file: Python # positions_board/migrations/0002_add_salary_field.py from django.db import migrations, models class Migration(migrations.Migration): # Pointer linking to the exact file that must execute directly before this script dependencies = [ ('positions_board', '0001_initial'), ] # The exact atomic actions the database engine must execute operations = [ migrations.AddField( model_name='enterpriserequisition', name='allotted_budget', field=models.DecimalField(decimal_places=2, default=0.0, max_digits=12), ), ] Schema Migrations vs. Data Migrations Django handles two fundamentally different types of migrations: Schema Migrations (Auto-generated): These alter the structural layout of your database—such as creating tables, removing columns, or updating indexes. Django manages these entirely on its own. Data Migrations (Manually written): These alter the actual records stored inside those structures —such as populating configuration constants, combining two text columns into one, or pre-filling data categories before launching an application. How to Build a Data Migration To write a data migration, you generate an empty migration shell file and add your data manipulation logic using Django's ORM: Bash # Step 1: Generate an empty, uniquely named migration shell file python manage.py makemigrations --empty positions_board --name populate_initial_tiers Open the generated file and use the RunPython action to safely loop through and save records inside the database: Python # positions_board/migrations/0003_populate_initial_tiers.py from django.db import migrations def initialize_default_categories(apps, schema_editor): # CRITICAL: Retrieve the model configuration safely through historical app states # instead of importing models directly. This avoids code-breaking dependency loops. JobCategory = apps.get_model('positions_board', 'JobCategory') # Run the transaction update loop JobCategory.objects.create(name="Engineering", code_prefix="ENG") JobCategory.objects.create(name="Product Management", code_prefix="PROD") class Migration(migrations.Migration): dependencies = [ ('positions_board', '0002_add_salary_field'), ] operations = [ # Executes your custom python data loop safely inside the migration sequence migrations.RunPython(initialize_default_categories), ] Advanced Migration Management: Rollbacks & Fixing Conflicts 1. Rolling Back Migrations If you apply a migration that causes issues, you can roll back your database state by passing the target application name followed by the number of the migration file you want to revert to . [Image diagram showing Django migration rollback mechanism moving the database schema backwards sequentially to a targeted migration number state] Bash # Roll back the database schema to the state it was in at file '0001_initial' python manage.py migrate positions_board 0001 Django reads the differences between your current state and target state, calculates the inversion rules, and cleanly rolls back any newer tables or columns. 2. Reverting All Migrations To completely wipe out all managed tables associated with an application without dropping your entire database, roll back to a baseline value of zero : Bash python manage.py migrate positions_board zero Using Fake Migrations ( --fake ) The --fake parameter updates Django’s migration tracking ledger table ( django_migrations ) without actually executing any SQL statements on your database hardware. Bash python manage.py migrate --fake When should you use Fake Migrations? Recovering from Manual Database Alterations: If a database administrator manually adds a column directly inside production SQL tools to fix an immediate crisis, Django's local migration script will fail when deployed, throwing an "OperationalError: column already exists" crash. Running --fake instructs Django to mark that migration script as "completed" in its tracking ledger, matching reality without crashing. Integrating Legacy Databases: When importing an existing pre-built database into a new Django project, you generate an initial migration blueprint representing the current layout. You then run migrate --fake so Django registers the tables as tracked without trying to create tables that already exist. Migrations Operations Architecture Matrix Command Variant / Concept Execution Scope Surface Primary Engineering Use Case Critical Guardrail Caution makemigrations Code Layer (Python Files). Translates changes in models.py into timestamped step scripts. Does not affect live data. Always check your generated scripts before committing code. migrate Database Layer (SQL Execution). Executes pending structural updates sequentially on live database tables. Backup your production database before running migrations to prevent accidental data loss. Data Migration Record Payload Layer. Handles pre-loading baseline constants, converting schemas, or running data cleanup loops. Never import models directly. Always retrieve data models using apps.get_model arguments. --fake Flag Ledger Configuration Only. Marks migration files as applied without running any SQL statements. Only use this tool when syncing manual database changes or importing pre-existing legacy tables.

Back to Django

Browse all study material on Careeroza