29
How to Name Django Migrations (and Why It's Important)
Have you ever needed to undo, fake, or deal with migrations more deeply than the typical python manage.py migrate
? Did you find yourself opening the migration files to find out what they contained? Everybody probably will at some point.
And if you have, you probably know what a pain it can be to go searching through all those files. In this post, we'll talk about the benefits of properly naming your Django migrations and show how doing a little prep work can save you -- and your fellow developers -- a lot of time. But before that, let's cover the basics.
Django migrations are a core part of the Django Object-Relational Mapper, commonly shortened to ORM. If you’re unfamiliar with ORM, it’s 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.
It's 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. But not all databases are equal when it comes to migrations. Django’s documentation states that some are more capable than others and it’s worth understanding the differences.
In terms of schema support, PostgreSQL is deemed the most capable option available. The lone exception is versions before PostgreSQL 11, which added columns with default values, causing a full rewrite of the table, for a time proportional to its size.
Django’s documentation recommends you always create new columns with null=True, as this way they will be added immediately.
Django’s migration documentation includes three suggestions when it comes to MySQL support.
- Roll-Back Risks: MySQL lacks support for transactions around schema alteration operations. If a migration fails, you will have to manually unpick the changes in order to try again. In short, if you find yourself here, it’s impossible to roll back with Django to an earlier point -- for that you’ll need to use raw SQL.
- Slow Execution Times: MySQL fully rewrites tables for almost every schema operation, causing resource usage consideration because it takes time proportional to the number of rows in the table to add or remove columns. On slower hardware, this can really drag out the process to an estimated minute per million rows. - adding a few columns to a table with just a few million rows could lock your site up for over ten minutes.
- Limited Name Lengths: MySQL has relatively small limits on name lengths for columns, tables and indexes, as well as a limit on the combined size of all columns an index covers. This means that indexes that are possible on other backends will fail to be created under MySQL.
SQLite lacks a lot robust built-in schema alteration support, forcing Django to step in an emulate it by:
- Creating a new table with the new schema
- Copying the data across
- Dropping the old table
- Renaming the new table to match the original name
While Django’s documentation is generally optimistic about how the process works in general, it also admits that results can come slowly and the outcome be “occasionally buggy”. It’s not recommended developers run and migrate SQLite in a production environment without first considering the risks and limitations.
Above nearly everything else, Django Migrations are important and helpful because they make developers' lives easier.
Databases are central components of an application and in today’s fast paced world, things change quickly. Agile projects are always changing and sometimes, adjustments are necessary to meet updated requirements. Django migrations help with the process of making, applying, and tracking changes to database schemas.
If you are working with Django then you are more than likely adept at coding in Python because the two go hand-in-hand. In Django, Migrations are written (primarily) in Python -- a language the developers know anyway -- so you’ll be working with a language you’re familiar with anyway.
Without Migrations, developers would have to have to learn SQL commands or work with a GUI like Phpmyadmin every time they wanted to change the model definition and modify the database schema.
Migrations are generated from the models you create so the process doesn’t have to be repeated. In comparison, building a model and then writing SQL to generate database tables is repetitive.
Database schemas don’t live in the code but Django Migrations are housed in the application, right in a folder with an appropriate name. So when you commit changes and make updates to the Migration, the changes are tracked.
One of the really helpful features of Django Migrations is that they can be named. You can attach a descriptive title to each migration in an effort to distinguish it from others. And if your project becomes complex and goes on for an extended period of time, there can be several migrations.
Now, there’s absolutely no issue with deciding to not name migrations. You can certainly get by without doing it.
Still, taking a minute to name Django Migrations is helpful if it only gives you an indication as to what’s inside. Just as it’s worthwhile to be descriptive in naming commits, branches, and nearly anything when it comes to version control in Git, naming Django Migrations can help you when you’re looking back through versions for a specific file.
You can either spend your time going through files one-by-one until you find what you’re looking for or you can see a list of properly named Django Migrations and get a good idea of where something is at first glance.
Clean code makes for easier maintenance. It just makes life a little easier.
Django’s makemigrations command has a flag --name
that can be used to make migrations more readable and easy on the eyes.
Sometimes Django will name your migrations for you but when it doesn't, the resulting title can be unhelpful when read by human beings. When Django names migrations, it comes out looking like this: 0005_auto_20210608_2154
.
What does that file contain? Who knows. Important stuff? Maybe. Non-essential parts? Your guess is as good as mine.
Alternatively, what if you took the time to name a migration like this: 0005_person_email_and_opt_out
? Even for someone unfamiliar with the project will probably be able to figure out what that migration contains.
Here’s a simple example I made. The first list only used python manage.py makemigrations:
0001_initial
0002_auto_20210608_2147
0003_auto_20210608_2149
0004_person_phone_number
0005_auto_20210608_2154
0006_auto_20210608_2155
Now below are the exact same migrations but with the --name
flag included in the makemigrations
command:
0001_initial
0002_business_address_fields
0003_business_owner_and_person
0004_person_phone_number
0005_person_email_and_opt_out
0006_business_description_and_services
Taking a small bit of time to name your migrations results in a more readable migration history which will make life easier in the future.
Look at the internal Django apps as an example. Every migration is named.
-
admin
0001_initial
0002_logentry_remove_auto_add
0003_logentry_add_action_flag_choices
-
auth
0001_initial
0002_alter_permission_name_max_length
0003_alter_user_email_max_length
0004_alter_user_username_opts
0005_alter_user_last_login_null
0006_require_contenttypes_0002
0007_alter_validators_add_error_messages
0008_alter_user_username_max_length
0009_alter_user_last_name_max_length
0010_alter_group_name_max_length
0011_update_proxy_permissions
0012_alter_user_first_name_max_length
-
contenttypes
0001_initial
0002_remove_content_type_name
It's that easy.
Django migrations go a long way in making a developer's life easier. But you can level up your game simply and make life easier for everyone when you give the migrations a descriptive, appropriate name. This post originally appeared on our insights blog, where we write about devops consulting services.
29