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 MigrationsWith 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 MigrationsDjango 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 MigrationsIt 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 MigrationsComing soon ...
#
Applying Django MigrationsYou 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 ModelsYour 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 MigrationsThis can be achieved by running the following command :
./manage.py showmigrations
#
Undo Django MigrationsNow 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 MigrationsSometimes 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 MigrationsSquashing 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 ConflictsWill be available very soon ...