You already understand the purpose of Django migrations and you’re familiar with fundamental usage patterns like creating and applying migrations. In this tutorial, you’ll dig deeper into the migration system and take a peek at some of its underlying mechanics, from the Django migrations table to the migration files themselves.

By the end of this article, you’ll know:

  • How Django keeps track of migrations
  • How migrations know which database operations to perform
  • How dependencies between migrations are defined

Once you’ve wrapped your head around this part of the Django migration system, you’ll be well prepared to create your own custom migrations. Let’s jump right in where we left off!

This article uses the bitcoin_tracker Django project built in Django Migrations: A Primer. You can either re-create that project by working through that article or you can download the source code:

The Django Migrations Table

Let’s recap how Django handles migrations. Assume that you’ve created a migration and then applied all available migrations with python manage.py migrate. If that command ran successfully, then your database tables now match your model’s definitions.

What happens if you run that command again? Let’s try it out:

Language: Shell
$ python manage.py migrate Operations to perform:  Apply all migrations: admin, auth, contenttypes, historical_data, sessions Running migrations:  No migrations to apply. 

Nothing happened! Once a migration has been applied to a database, Django will not apply this migration to that particular database again. Ensuring that a migration is applied only once requires keeping track of the migrations that have been applied.

Django uses a database table called django_migrations. Django automatically creates this table in your database the first time you apply any migrations. For each migration that’s applied or faked, a new row is inserted into the table.

For example, here’s what this table looks like in our bitcoin_tracker project:

ID App Name Applied
1 contenttypes 0001_initial 2019-02-05 20:23:21.461496
2 auth 0001_initial 2019-02-05 20:23:21.489948
3 admin 0001_initial 2019-02-05 20:23:21.508742
4 admin 0002_logentry_remove... 2019-02-05 20:23:21.531390
5 admin 0003_logentry_add_ac... 2019-02-05 20:23:21.564834
6 contenttypes 0002_remove_content_... 2019-02-05 20:23:21.597186
7 auth 0002_alter_permissio... 2019-02-05 20:23:21.608705
8 auth 0003_alter_user_emai... 2019-02-05 20:23:21.628441
9 auth 0004_alter_user_user... 2019-02-05 20:23:21.646824
10 auth 0005_alter_user_last... 2019-02-05 20:23:21.661182
11 auth 0006_require_content... 2019-02-05 20:23:21.663664
12 auth 0007_alter_validator... 2019-02-05 20:23:21.679482
13 auth 0008_alter_user_user... 2019-02-05 20:23:21.699201
14 auth 0009_alter_user_last... 2019-02-05 20:23:21.718652
15 auth 0010_alter_group_nam... 2019-02-05 20:23:21.721038
16 auth 0011_update_proxy_pe... 2019-02-05 20:23:21.723517
17 auth 0012_alter_user_firs... 2019-02-05 20:23:21.724902
18 historical_data 0001_initial 2019-02-05 20:23:21.726000
19 sessions 0001_initial 2019-02-05 20:23:21.734611
22 historical_data 0002_switch_to_decimals 2019-02-05 20:30:11.337894

As you can see, there is an entry for each applied migration. The table not only contains the migrations from our historical_data app, but also the migrations from all other installed apps.

The next time migrations are run, Django will skip the migrations listed in the database table. This means that, even if you manually change the file of a migration that has already been applied, Django will ignore these changes, as long as there’s already an entry for it in the database.

You could trick Django into re-running a migration by deleting the corresponding row from the table, but this is rarely a good idea and can leave you with a broken migration system.

The Migration File

What happens when you run python manage.py makemigrations <appname>? Django looks for changes made to the models in your app <appname>. If it finds any, like a model that has been added, then it creates a migration file in the migrations subdirectory. This migration file contains a list of operations to bring your database schema in sync with your model definition.

Read the full article at https://realpython.com/digging-deeper-into-migrations/ »


[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]