Colibri Software

Django Migrations

By ILLIA

Django Migrations

Introduction

Migrations are used by Django to track and push changes in models (creating/deleting a model, adding a field, etc.) to the database. Here are four essential commands that are used when working with database migrations:

  1. makemigrations – is used to create new migrations based on changes made in database models.
  2. migrate – is used to apply and revert existing migrations.
  3. sqlmigrate – is used to display SQL statement based on migration.
  4. showmigrations – is used to list projects migrations and their status.

makemigrations and migrate commands are the most essential commands. Migration files are located in the migrations directory of each app. Any changes in models require the creation of new migrations. The basic workflow after creating or modifying models with sample output:

$ python manage.py makemigrations

Sample output
Migrations for 'pets':
  pets/migrations/0002_auto.py:
    - Alter field owner on pet
$ python manage.py migrate

Sample output
Operations to perform:
  Apply all migrations: pets
Running migrations:
  Rendering model states... DONE
  Applying pets.0002_auto... OK

Dependencies

When creating a migration for one app that relates to a model from another app (e.g. app pets requires a ForeignKey from owners table), this migration will be created with a dependency on the migration of the second app. This means that migrations from app 2 should run before migrations from app 1, which are dependent on them. Essentially, this is the way to tell Django to run something else before running this migration.

Migration files

Migration file contains a class that extends django.db.migrations.Migration class and have 4 attributes, 2 of those are the most used:

  • dependencies – is a list of migrations that current migration depends on
  • operations – a list of Operation classes that define what this migration does

A basic migration file looks like this:

from django.db import migrations

class Migration(migrations.Migration):

    dependencies = [
        ('pets', '0001_initial'),
    ]

    operations = [
        migrations.RenameField(
            model_name='pet',
            old_name='owners',
            new_name='owner',
        ),
    ]

Operations are most essential because they are a set of declarative instructions that specify what changes in the database schema have to be made. Migration can have initial = True attribute which shows that this migration is the initial one. Initial migration is the one that creates the first version of apps database tables.

Migrations are used by Django to track and push changes in models (creating/deleting a model, adding a field, etc.) to the database.

History consistency

Inconsistent migration history is when migration has been applied but some of its dependencies have not. This is a strong indication that the dependencies are incorrect, so Django will not run these migrations or make new migrations until this issue has been fixed.

Reversing migrations

These two commands are used to reverse the migrations that have already been applied:

  • python manage.py migrate – migrates app to the specified migration. If the app is ahead of this migration, then the command would reverse all migrations back to this point. Otherwise, if the app is behind this migration, then the previous migrations will be applied up until this migration. Usage and sample output:
    $ python manage.py migrate pets 0002
    Operations to perform:
    Target specific migration: 0002_auto, from pets
    Running migrations:
    Rendering model states... DONE
    Unapplying pets.0003_auto... OK
  • python manage.py migrate zero – reverses all migrations. Usage and sample output:
    $ python manage.py migrate pets zero
    Operations to perform:
    Unapply all migrations: pets
    Running migrations:
    Rendering model states... DONE
    Unapplying pets.0002_auto... OK
    Unapplying pets.0001_initial... OK

Squashing migrations

You can create as many migrations as you want; however, the project can become very messy with a lot of migration files that may contain actions that neutralize each other (e.g. add a field in one migration, remove that field in another). In order to prevent this, migrations can be squashed and the new migration will apply all changes created in previous migrations. Here is a command to do it:

python manage.py squashmigrations  

Usage and sample output:

$ ./manage.py squashmigrations pets 0004
Will squash the following migrations:
 - 0001_initial
 - 0002_some_change
 - 0003_another_change
 - 0004_undo_something
Do you wish to proceed? [y/N] y
Optimizing...
  Optimized from 12 operations to 7 operations.
Created new squashed migration /home/bob/Programs/DjangoTest/test/migrations/0001_squashed_0004_undo_somthing.py
  You should commit this migration but leave the old ones in place;
  the new migration will be used for new installs. Once you are sure
  all instances of the codebase have applied the migrations you squashed,
  you can delete them.

Steps to do after you squashed migrations:

  1. Commit the new squashed migration with all other migrations it replaces :white_check_mark:
  2. Delete all migration files that were replaced with a new one :white_check_mark:
  3. Update all dependent migrations to use the new squashed one :white_check_mark:
  4. Remove replaces attribute in Migration class of the squashed migration :white_check_mark:
  5. Commit these changes :white_check_mark:

For more information, please visit Django Documentation.