Skip to main content

Django Migrations

What are Django Migrations ?#

Django migrations are a core part of the Django Object-Relational Mapper, commonly shortened to ORM. If you are unfamiliar with ORM, it is one of Django’s powerful features which enables you to interact with your database, like you would with SQL.

The migration framework was brought into Django on version 1.7. Migrations themselves consist of Python code and define the operations to apply to a database schema as it progresses through the Software Development Life Cycle, or SDLC. These migration files for each application live in a migrations directory within the app and, according to Django documentation, are designed to be committed to, and distributed as part of, its codebase.

Django documentation tells us that migrations should be made once on your development machine and then running the same migrations on your colleagues machines, your staging machines, and eventually your production machines.

It is helpful to think of migrations as a version control system for your database schema.

In terms of backend support, migrations are supported on any backends that Django ships with, including third-party backends that have support for schema alteration.

What is so great about Django Migrations ?#

Django Migrations are important and helpful because they make developers' lives easier because of the following advantages :

  • Django Migrations help with Agility
  • Developers use Python for writing Django Migrations
  • Django Migrations help reduce repetition
  • Django Migrations benefit from Version Control (Git)

Creating Django Migrations#

With the model created, the first thing you need to do is create a migration for it. You can do this with the management command makemigrations :

./manage.py makemigrations app_name

Note : Specifying the name of the application app_name is optional. Leaving it off creates migrations for all apps.

This creates the migrations file that instructs Django on how to create the database tables for the models defined in your application.

Naming Django Migrations#

Django came up with a naming convention based on the timestamp. So, The makemigrations command automatically generates something like 0002_auto_20210815_2157 as migration name.

If you want to give the migration a custom and meaningful name, you can use the --name option.

To do that :

  • Remove the old migration : rm app_name/migrations/generated_migration_name.py
  • Generate the new one : ./manage.py makemigrations app_name --name custom_migration_name (custom_migration_name without .py extension)

Linting Django Migrations#

It detects backward incompatible migrations and can be integrated nicely into CI pipeline or directly into tests.

This django-migration-linter package is one of the most popular tools.

Testing Django Migrations#

Coming soon ...

Applying Django Migrations#

You have now created the migration, but to actually make any changes in the database, you have to apply it with the management command migrate :

./manage.py migrate

By default, the migrate management command applies the migrations for all installed apps from INSTALLED_APPS setting.

After the migration has been successfully applied, you can make a verification on the database with the management command dbshell :

./manage.py dbshell

select * from django_migrations;

If we run the management command ./manage.py migrate app_name again, we can verify that there are no migrations to apply :

Operations to perform: Apply all migrations: app_nameRunning migrations: No migrations to apply.

Changing Django Models#

Your models are not set in stone. Your models will change as your Django project gains more features. You might add or remove fields or change their types and options.

When you change the definition of a model, the database tables used to store these models have to be changed too. If your model definitions do not match your current database schema, you will most likely run into a django.db.utils.OperationalError error.

So, you should run the management command makemigrations again :

./manage.py makemigrations

Then apply the new changes to database schema by running the management command migrate :

./manage.py migrate

Listing Django Migrations#

This can be achieved by running the following command :

./manage.py showmigrations

Undo Django Migrations#

Now you know how to make changes to your database schema by creating and applying migrations. At some point, you might want to undo changes and switch back to an earlier database schema because you :

  • Want to test a migration a colleague wrote
  • Realize that a change you made was a bad idea
  • Work on multiple features with different database changes in parallel
  • Want to restore a backup that was created when the database still had an older schema

This can be achieved by running the management command :

./manage.py migrate app_name migration_name

Note : migration_name can be something like 0001_initial

Reset Django Migrations#

Sometimes you need to remove all django migrations and re-create them. You can achieve this by following the next steps :

  • Remove migration files from source code (for all apps)
  • Generate new migration files for each app with ./manage.py makemigrations app_name command
  • Disable the migrate step in the continuous integration workflow temporarily
  • Deploy the new changes
  • Login to database : ./manage.py dbshell
  • Delete all django migrations : delete from django_migrations;
  • Fake the django migrations : ./manage.py migrate --fake
  • Check the new status of django migrations : select * from django_migrations;

If the migrate operation was successful and django_migrations table contains all newly created migrations, enable it in CI workflow and redeploy again

Note :#

Apply this section carefully. Use the Squash section instead.

Squash Django Migrations#

Squashing or combining django migrations optimize database migrations.

To do so, Django has /manage.py squachmigrations management command that allows you to squash or combine multiple migration files into a single optimized migration file.

Let's say you have a Django app named app_name which has 5 migrations

- 0001_initial- 0002_change_2- 0003_change_3- 0004_change_4- 0004_change_5

Applying these migrations may take time, so you can squash and optimize them with the management command ./manage.py squashmigrations app_name 0005_change_5 (or just prefix 0005 instead of 0005_change_5).

You will be asked for confirmation.

Will squash the following migrations:- 0001_initial- 0002_change_2- 0003_change_3- 0004_change_4- 0004_change_5Do you wish to proceed? [yN] yOptimizing...  Optimized from 9 operations to 4 operations.Created new squashed migration ABSOLUTE_PATH_TO_app_name/migrations/0001_squashed_0005_change_5.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.

Read more about squashing django migrations

Manage Conflicts#

Will be available very soon ...

Last updated on by lemaadi