Django Migration Framework

Django Release History

South First Release

Django Migration = Schema Migration + Data Migration

Forward Migration

Backward Migration

Part 1

Closed System Observation

Book Counter

#books/models.py
from django.db import models

# Create your models here.


class Author(models.Model):
    first_name = models.CharField(max_length=255, blank=False, null=False)
    middle_name = models.CharField(max_length=255, blank=True, null=True,
                                   default='')
    last_name = models.CharField(max_length=255, blank=True, null=True,
                                 default='')
    profile_photo = models.URLField(default='', blank=True, null=True)


class Book(models.Model):
    name = models.CharField(max_length=255, blank=False, null=False)
    cover_photo = models.URLField(default='', blank=True, null=True)
    language = models.CharField(max_length=255, blank=True, null=False)

Book App

Important Migration Commands

  • makemigrations
  • showmigrations
  • sqlmigrate
  • squashmigrations
  • migrate

makemigrations

$python manage.py makemigrations book
# Generated by Django 3.2.5 on 2021-07-12 20:09

from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = []

    operations = [
        migrations.CreateModel(
            name='Author',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, 
                                           serialize=False, verbose_name='ID')),
                ('first_name', models.CharField(max_length=255)),
                ('middle_name', models.CharField(blank=True, default='', max_length=255, 
                                                 null=True)),
                ('last_name', models.CharField(blank=True, default='', max_length=255, 
                                               null=True)),
                ('profile_photo', models.URLField(blank=True, default='', null=True)),
            ],
        ),
        migrations.CreateModel(
            name='Book',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True,
                                           serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=255)),
                ('cover_photo', models.URLField(blank=True, default='', null=True)),
                ('language', models.CharField(blank=True, max_length=255)),
            ],
        ),
    ]

showmigrations

$python manage.py showmigrations
admin
 [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
 [X] 0003_logentry_add_action_flag_choices
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
 [X] 0003_alter_user_email_max_length
 [X] 0004_alter_user_username_opts
 [X] 0005_alter_user_last_login_null
 [X] 0006_require_contenttypes_0002
 [X] 0007_alter_validators_add_error_messages
 [X] 0008_alter_user_username_max_length
 [X] 0009_alter_user_last_name_max_length
 [X] 0010_alter_group_name_max_length
 [X] 0011_update_proxy_permissions
 [X] 0012_alter_user_first_name_max_length
book
 [ ] 0001_initial
contenttypes
 [X] 0001_initial
 [X] 0002_remove_content_type_name
sessions
 [X] 0001_initial

sqlmigrate

$python manage.py sqlmigrate book 0001
BEGIN;
--
-- Create model Author
--
CREATE TABLE "book_author" ("id" bigserial NOT NULL PRIMARY KEY, 
                            "first_name" varchar(255) NOT NULL, 
                            "middle_name" varchar(255) NULL, 
                            "last_name" varchar(255) NULL, 
                            "profile_photo" varchar(200) NULL);
--
-- Create model Book
--
CREATE TABLE "book_book" ("id" bigserial NOT NULL PRIMARY KEY, 
                          "name" varchar(255) NOT NULL, 
                          "cover_photo" varchar(200) NULL, 
                          "language" varchar(255) NOT NULL);
COMMIT;
$python manage.py sqlmigrate book 0001_i --backwards
BEGIN;
--
-- Create model Book
--
DROP TABLE "book_book" CASCADE;
--
-- Create model Author
--
DROP TABLE "book_author" CASCADE;
COMMIT;

Backward migration

Add a new field


# books/models.py
class Book(models.Model):
    ...
    authors = models.ManyToManyField(Author, related_name="books")
$python manage.py makemigrations
Migrations for 'book':
  book/migrations/0002_book_authors.py
    - Add field authors to book
$python manage.py showmigrations book
book
 [ ] 0001_initial
 [ ] 0002_book_authors
# Generated by Django 3.2.5 on 2021-07-12 20:16

from django.db import migrations, models


class Migration(migrations.Migration):

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

    operations = [
        migrations.AddField(
            model_name='book',
            name='authors',
            field=models.ManyToManyField(related_name='books', 
                                         to='book.Author'),
        ),
    ]

0002

$python manage.py sqlmigrate book 0002
BEGIN;
--
-- Add field authors to book
--
CREATE TABLE "book_book_authors" ("id" bigserial NOT NULL PRIMARY KEY, 
                                  "book_id" bigint NOT NULL, 
                                  "author_id" bigint NOT NULL);
ALTER TABLE "book_book_authors" 
ADD CONSTRAINT "book_book_authors_book_id_author_id_652071c2_uniq"
UNIQUE ("book_id", "author_id");

ALTER TABLE "book_book_authors" 
ADD CONSTRAINT "book_book_authors_book_id_2a4a45bb_fk_book_book_id" 
FOREIGN KEY ("book_id") REFERENCES "book_book" ("id") DEFERRABLE INITIALLY DEFERRED;

ALTER TABLE "book_book_authors" 
ADD CONSTRAINT "book_book_authors_author_id_dc6a47c1_fk_book_author_id" 
FOREIGN KEY ("author_id") REFERENCES "book_author" ("id") DEFERRABLE INITIALLY DEFERRED;

CREATE INDEX "book_book_authors_book_id_2a4a45bb" ON "book_book_authors" ("book_id");

CREATE INDEX "book_book_authors_author_id_dc6a47c1" ON "book_book_authors" ("author_id");

COMMIT;

DEFERRABLE INITIALLY DEFERRED

begin;
insert into django_admin_log values(, 23, ); 
-- 23 is not present in the content type

insert into content_type values("post")
--this one get the id 23

commit; 
-- now postgres immediatley checks all the foreign key 
-- constraints are matched. By default postres 
-- checks at the end of the statement.
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='Token',
            fields=[
                ('key', models.CharField(primary_key=True, serialize=False, 
                                         max_length=40)),
                ('created', models.DateTimeField(auto_now_add=True)),
                ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, 
                                              related_name='auth_token', 
                                              on_delete=models.CASCADE)),
            ],
            options={
            },
            bases=(models.Model,),
        ),
    ]

Third-party migration

squashmigrations

$python manage.py showmigrations book
book
 [ ] 0001_initial
 [ ] 0002_book_authors
$python manage.py squashmigrations book 0002
Will squash the following migrations:
 - 0001_initial
 - 0002_book_authors
Do you wish to proceed? [yN] y
Optimizing...
  Optimized from 3 operations to 2 operations.
Created new squashed migration 
/Users/user/code/personal/book_counter/book/migrations/0001_squashed_0002_book_authors.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.
# book/migrations/0001_squashed_0002_book_authors.py

from django.db import migrations, models

class Migration(migrations.Migration):
    replaces = [('book', '0001_initial'), ('book', '0002_book_authors')]
    initial = True
    dependencies = []

    operations = [
        migrations.CreateModel(
            name='Author',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, 
                serialize=False, verbose_name='ID')),
                ('first_name', models.CharField(max_length=255)),
                ('middle_name', models.CharField(blank=True, default='', 
                                                 max_length=255, null=True)),
                ('last_name', models.CharField(blank=True, default='',
                                               max_length=255, null=True)),
                ('profile_photo', models.URLField(blank=True, default='', null=True)),
            ],
        ),
        migrations.CreateModel(
            name='Book',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, 
                serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=255)),
                ('cover_photo', models.URLField(blank=True, default='', null=True)),
                ('language', models.CharField(blank=True, max_length=255)),
                ('authors', models.ManyToManyField(related_name='books', to='book.Author')),
            ],
        ),
    ]
$python manage.py sqlmigrate book 0001_squ
BEGIN;
--
-- Create model Author
--
CREATE TABLE "book_author" ("id" bigserial NOT NULL PRIMARY KEY, 
                            "first_name" varchar(255) NOT NULL, 
                            "middle_name" varchar(255) NULL, 
                            "last_name" varchar(255) NULL, 
                            "profile_photo" varchar(200) NULL);
--
-- Create model Book
--
CREATE TABLE "book_book" ("id" bigserial NOT NULL PRIMARY KEY, 
                          "name" varchar(255) NOT NULL, 
                          "cover_photo" varchar(200) NULL, 
                          "language" varchar(255) NOT NULL);
CREATE TABLE "book_book_authors" ("id" bigserial NOT NULL PRIMARY KEY, 
                                  "book_id" bigint NOT NULL, 
                                  "author_id" bigint NOT NULL);
ALTER TABLE "book_book_authors" 
ADD CONSTRAINT "book_book_authors_book_id_author_id_652071c2_uniq" 
UNIQUE ("book_id", "author_id");

ALTER TABLE "book_book_authors" ADD CONSTRAINT "book_book_authors_book_id_2a4a45bb_fk_book_book_id"
FOREIGN KEY ("book_id") REFERENCES "book_book" ("id") DEFERRABLE INITIALLY DEFERRED;

ALTER TABLE "book_book_authors" ADD CONSTRAINT "book_book_authors_author_id_dc6a47c1_fk_book_author_id" 
FOREIGN KEY ("author_id") REFERENCES "book_author" ("id") DEFERRABLE INITIALLY DEFERRED;

CREATE INDEX "book_book_authors_book_id_2a4a45bb" ON "book_book_authors" ("book_id");
CREATE INDEX "book_book_authors_author_id_dc6a47c1" ON "book_book_authors" ("author_id");
COMMIT;

Add a new model

# shelf/models.py
from book.models import Book


class Shelf(models.Model):
    name = models.CharField(max_length=255, 
                            unique=True,
                            db_index=True, 
                            null=False, 
                            blank=False)
    books = models.ManyToManyField(Book, 
                                   related_name="shelves")

# shelf/migrations/0001_initial.py
# Generated by Django 3.2.5 on 2021-07-12 20:41

from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
        ('book', '0001_squashed_0002_book_authors'),
    ]

    operations = [
        migrations.CreateModel(
            name='Shelf',
            fields=[
                ('id', models.BigAutoField(auto_created=True, 
                                           primary_key=True, 
                                           serialize=False, 
                                           verbose_name='ID')),
                ('name', models.CharField(db_index=True, 
                                          max_length=255, 
                                          unique=True)),
                ('books', models.ManyToManyField(related_name='shelves', 
                                                 to='book.Book')),
            ],
        ),
    ]

migrate --plan

Plan

$python manage.py migrate shelf --plan
Planned operations:
book.0001_squashed_0002_book_authors
    Create model Author
    Create model Book
shelf.0001_initial
    Create model Shelf

migrate

Part 2

Internals

Django Migrations

By Kracekumar

Django Migrations

  • 211