DjangoMigrations

Parameters

django-admin commandDetails
makemigrations <my_app>Generate migrations for my_app
makemigrationsGenerate migrations for all apps
makemigrations --mergeResolve migration conflicts for all apps
makemigrations --merge <my_app>Resolve migration conflicts for my_app
makemigrations --name <migration_name> <my_app>Generate a migration for my_app with the name migration_name
migrate <my_app>Apply pending migrations of my_app to the database
migrateApply all pending migrations to the database
migrate <my_app> <migration_name>Apply or unapply up to migration_name
migrate <my_app> zeroUnapply all migrations in my_app
sqlmigrate <my_app> <migration_name>Prints the SQL for the named migration
showmigrationsShows all migrations for all apps
showmigrations <my_app>Shows all migrations in my_app

Working with migrations

Django uses migrations to propagate changes you make to your models to your database. Most of the time django can generate them for you.

To create a migration, run:

$ django-admin makemigrations <app_name>

This will create a migration file in the migration submodule of app_name. The first migration will be named 0001_initial.py, the other will start with 0002_, then 0003, ...

If you omit <app_name> this will create migrations for all your INSTALLED_APPS.

To propagate migrations to your database, run:

$ django-admin migrate <app_name>

To show all your migrations, run:

$ django-admin showmigrations app_name
app_name
  [X] 0001_initial
  [X] 0002_auto_20160115_1027
  [X] 0003_somemodel
  [ ] 0004_auto_20160323_1826
  • [X] means that the migration was propagated to your database
  • [ ] means that the migration was not propagated to your database. Use django-admin migrate to propagate it

You call also revert migrations, this can be done by passing the migration name to the migrate command. Given the above list of migrations (shown by django-admin showmigrations):

$ django-admin migrate app_name 0002  # Roll back to migration 0002
$ django-admin showmigrations app_name
app_name
  [X] 0001_initial
  [X] 0002_auto_20160115_1027
  [ ] 0003_somemodel
  [ ] 0004_auto_20160323_1826

Manual migrations

Sometimes, migrations generated by Django are not sufficient. This is especially true when you want to make data migrations.

For instance, let's you have such model:

class Article(models.Model):
    title = models.CharField(max_length=70)

This model already have existing data and now you want to add a SlugField:

class Article(models.Model):
    title = models.CharField(max_length=70)
    slug = models.SlugField(max_length=70)

You created the migrations to add the field, but now you would like to set the slug for all existing article, according to their title.

Of course, you could just do something like this in the terminal:

$ django-admin shell
>>> from my_app.models import Article
>>> from django.utils.text import slugify
>>> for article in Article.objects.all():
...     article.slug = slugify(article.title)
...     article.save()
...
>>>

But you will have to do this in all your environments (ie. your office desktop, your laptop, ...), all your coworkers will have to do so as well, and you will have to think about it on staging and when pushing live.

To make it once and for all, we will do it in a migration. First create an empty migration:

$ django-admin makemigrations --empty app_name

This will create an empty migration file. Open it, it contains an base skeleton. Let's say your previous migration was named 0023_article_slug and this one is named 0024_auto_20160719_1734. Here is what we will write in our migration file:

# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-07-19 15:34
from __future__ import unicode_literals

from django.db import migrations
from django.utils.text import slugify


def gen_slug(apps, schema_editor):
    # We can't import the Article model directly as it may be a newer
    # version than this migration expects. We use the historical version.
    Article = apps.get_model('app_name', 'Article')
    for row in Article.objects.all():
        row.slug = slugify(row.name)
        row.save()


class Migration(migrations.Migration):

    dependencies = [
        ('hosting', '0023_article_slug'),
    ]

    operations = [
        migrations.RunPython(gen_slug, reverse_code=migrations.RunPython.noop),
        # We set `reverse_code` to `noop` because we cannot revert the migration
        # to get it back in the previous state.
        # If `reverse_code` is not given, the migration will not be reversible,
        # which is not the behaviour we expect here.
    ]

Fake migrations

When a migration is run, Django stores the name of the migration in a django_migrations table.

Create and Fake initial migrations for existing schema

If your app already has models and database tables, and doesn’t have migrations. First create initial migrations for you app.

python manage.py makemigrations your_app_label

Now fake initial migrations as applied

python manage.py migrate --fake-initial

Fake all migrations in all apps

python manage.py migrate --fake

Fake single app migrations

python manage.py migrate --fake core

Fake single migration file

python manage.py migrate myapp migration_name

Custom names for migration files

Use the makemigrations --name <your_migration_name> option to allow naming the migrations(s) instead of using a generated name.

python manage.py makemigrations --name <your_migration_name> <app_name>

Solving migration conflicts

Introduction

Sometimes migrations conflict, resulting in making the migration unsuccesful. This can happen in a lot of scenerio's, however it can occur on a regular basis when developing one app with a team.

Common migration conflicts happen while using source control, especially when the feature-per-branch method is used. For this scenario we will use a model called Reporter with the attributes name and address.

Two developers at this point are going to develop a feature, thus they both get this initial copy of the Reporter model. Developer A adds an age which results in the file 0002_reporter_age.py file. Developer B adds a bank_account field which resulsts in 0002_reporter_bank_account. Once these developers merge their code together and attempt to migrate the migrations, a migration conflict occurred.

This conflict occurs because these migrations both alter the same model, Reporter. On top of that, the new files both start with 0002.

Merging migrations

There are several ways of doing it. The following is in the recommended order:

  1. The most simple fix for this is by running the makemigrations command with a --merge flag.

    python manage.py makemigrations --merge <my_app>
    

    This will create a new migration solving the previous conflict.

  2. When this extra file is not welcome in the development environment for personal reasons, an option is to delete the conflicting migrations. Then, a new migration can be made using the regular makemigrations command. When custom migrations are written, such as migrations.RunPython, need to be accounted for using this method.

Change a CharField to a ForeignKey

First off, let's assume this is your initial model, inside an application called discography:

from django.db import models

class Album(models.Model):
    name = models.CharField(max_length=255)
    artist = models.CharField(max_length=255)

Now, you realize that you want to use a ForeignKey for the artist instead. This is a somewhat complex process, which has to be done in several steps.

Step 1, add a new field for the ForeignKey, making sure to mark it as null (note that the model we are linking to is also now included):

from django.db import models

class Album(models.Model):
    name = models.CharField(max_length=255)
    artist = models.CharField(max_length=255)
    artist_link = models.ForeignKey('Artist', null=True)

class Artist(models.Model):
    name = models.CharField(max_length=255)

...and create a migration for this change.

./manage.py makemigrations discography

Step 2, populate your new field. In order to do this, you have to create an empty migration.

./manage.py makemigrations --empty --name transfer_artists discography

Once you have this empty migration, you want to add a single RunPython operation to it in order to link your records. In this case, it could look something like this:

def link_artists(apps, schema_editor):
    Album = apps.get_model('discography', 'Album')
    Artist = apps.get_model('discography', 'Artist')
    for album in Album.objects.all():
        artist, created = Artist.objects.get_or_create(name=album.artist)
        album.artist_link = artist
        album.save()

Now that your data is transferred to the new field, you could actually be done and leave everything as is, using the new artist_link field for everything. Or, if you want to do a bit of cleanup, you want to create two more migrations.

For your first migration, you will want to delete your original field, artist. For your second migration, rename the new field artist_link to artist.

This is done in multiple steps to ensure that Django recognizes the operations properly.