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:
Download Source Code: Click here to download the code for the Django migrations project you’ll be using in this article.
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:
$ 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 ]