Well you add a new model to one of your apps, you add a new field to a model, you even change the help test for a model... Any of these changes can cause the need for a migration. How often how you started up your django project locally and it yells at you:
Your models have changes that are not yet reflected in a migration, and so won't be applied.
Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.
When you see this you need to do as it says and run:
$ python manage.py makemigrations [app_label]
If you have migrations in work in other apps at the same time, you can also specify an app label, no app label, it assumes you mean everything!
After this you should see a a new file in the appropriate migrations folder for the app model you modified. The migration should be prefixed with a number and an auto
label, followed by the date. As long as it has been created successfully, you may now run the migration.
Note : If you name the migration by doing a --name
during the command will override the auto and date schema django uses, the number should still be there!
Now that you have an automatic migration verified let's apply it. In the terminal just type:
$ python manage.py migrate [app_label]
As with all other migration commands, this one too accepts the app_label
parameter which will only apply migrations to that particular app. If it is left off then all pending migrations are applied. If you would like to simulate what would happen when running multiple migrations that depend on each other you can submit the --dry-run
paramter here and it will just show the output without actually applying it.
Oh no I didn't mean to make that change! And now my database has been updated! Ugh!! Curses!! Better just throw the computer out and start new or.....
$ python manage.py migrate [app_label] [migration_name]
All automatically created migrations can be automatically unapplied. Let us say you have the following migrations.
0004_auto_20191114_1934.py
0005_auto_20200127_2309.py
Currently 0005 is applied but it is a mistake and we need to roll it back (in this situation our app name is core
). We can type:
$ python manage.py migrate core 0004_auto_20191114_1934
Running this command will roll your database back to the schema state it was at after migration 0004_auto_20191114_1934
You can now delete the 0005_auto_20200127_2309.py
file and then fix the models and regenerate the migrations. Once that's all done we can run run the migrate command to get back up to speed.
Note: you do not have to use the file name to specify which migrations, you should be able to use the number prefix so python manage.py migrate core 0004
should do the trick!
Sometimes you need to manipulate the structure of your data, or modify it's contents for whatever reason. Migrations are a common place for that to happen. Let us look at an example of needing to change the model id because of an initial misspelling that definitely didn't happen to me in real life...
Let's generate an empty migration:
$ python manage.py makemigrations core --name fix_model_id --empty
This command is a bit different from regular migration creation. You see that we had the app label core
is a required parameter here as we have to tell django which app we are trying to make a migration for. the --name
parameter isn't strictly required but I like to fill it in for context so I can easily tell auto migrations from manual ones. After this command is run you will see a new file in your migrations folder named 0006_fix_model_id.py
(number subject to change). Upon opening the file we find a basic template migration.
# Generated by Django 2.2.10 on 2020-02-17 00:49
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0005_auto_20200127_2309'),
]
operations = [
]
Django has already filled the required dependencies in here, although if you have special requirements, you can specify them as well or in place of if needed. As long as it's a valid app in the project you can depend on any migration from any app if needed. Let's fill in some basics here and look at two examples:
Example A:
Generally this is more for logical operations that make relationships via foreign keys or change schema manually in some way. Notice that we have a forwards
function and a backwards
function defined. When doing custom migrations it is generally always recommend to write a backwards function to reverse the changes if you need to roll this migration back.
# Generated by Django 2.2.10 on 2020-02-17 00:49
import django.contrib.postgres.fields.jsonb
from django.db import migrations, models
def forwards(apps, schema_editor):
Book = apps.get_model("core", "Book")
Book.objects.filter(author="Douglas AAdams").update(author="Douglas Adams")
def backwards(apps, schema_editor):
Book = apps.get_model("core", "Book")
Book.objects.filter(author="Douglas Adams").update(author="Douglas AAdms")
class Migration(migrations.Migration):
dependencies = [
("core", "0005_auto_20200127_2309"),
]
operations = [
migrations.RunPython(forwards, backwards),
]
Notice once more that at the end we call a function migration.RunPython()
with both the forwards and backwards functions. The migration system built into django will understand this and run the appropriate function when you invoke the migration command.
Example B:
Under some circumstances it may not make sense to write a reverse migration. Sometimes it's an emergency, sometimes you want a change to be permanent. For example if the above choice we want it to be permanent. What do we do? Well we issue a noop
or no operation.
# Generated by Django 2.2.10 on 2020-02-17 00:49
import django.contrib.postgres.fields.jsonb
from django.db import migrations, models
def forwards(apps, schema_editor):
Book = apps.get_model("core", "Book")
Book.objects.filter(author="Douglas AAdams").update(author="Douglas Adams")
class Migration(migrations.Migration):
dependencies = [
("core", "0005_auto_20200127_2309"),
]
operations = [
migrations.RunPython(forwards, migrations.RunPython.noop),
]
As you can see we used a function under migrations.RunPython
in order to tell django to do nothing on the reverse. I suppose you could do this for backwards migrations, i.e noop a forwards and only have a backwards but I've never experienced it.
Many times it's useful to show which migrations have been applied and which are unapplied. As always app label is only necessary if you want to filter the results down to one app.
python manage.py showmigrations [app_label]
This is only a very surface level overview of migrations. Migrations can get very very complicated depending on your schema. You will also find gotcha's like for example computed properties are not available to instantiated app classes when using migrations so you may have to duplicate some of that logic yourself. The reasons for this are such that I'm not even sure why, but I know it to be true. There are also other tools to help manage migrations. I would run the following command and see what you get. There are so many built in commands and you can even make your own for your apps!
$ python manage.py
ππ°π