Migrations in Django are auto-generated files from the models of an application that apply changes to a stricture of a Table whether it's the creation, the modification, or the deletion of a field.
There are some situations when you are adding a new field or doing some modifications that we'll require you to write custom migrations.π€
Let's see.
Problem
We have an e-commerce application selling shoes and computers. (Weird example but that will do the workπ)
In the database, we have the two following tables.
from django.db import models
class Shoe(models.Model):
name = models.CharField(max_length=100)
brand = models.CharField(max_length=100)
color = models.CharField(max_length=100)
price = models.IntegerField()
in_stock = models.BooleanField(default=True)
description = models.TextField()
image = models.ImageField(upload_to='images/')
def __str__(self):
return self.name
class Computer(models.Model):
name = models.CharField(max_length=100)
brand = models.CharField(max_length=100)
color = models.CharField(max_length=100)
price = models.IntegerField()
in_stock = models.BooleanField(default=True)
description = models.TextField()
image = models.ImageField(upload_to='images/')
def __str__(self):
return self.name
We have the Computer
table and the Shoe
table with basically the same fields. A way to improve this and avoid DRY will be to create an AbstractModel
class but well in the database that won't count that much.
Let's create a class called Product
that will have the same fields and a product_type
field to know the type of product, computer
or shoe
.
class Product(models.Model):
PRODUCT_TYPE_CHOICES = (
('shoe', 'shoe'),
('computer', 'shoe'),
)
name = models.CharField(max_length=100)
brand = models.CharField(max_length=100)
color = models.CharField(max_length=100)
price = models.IntegerField()
in_stock = models.BooleanField(default=True)
description = models.TextField()
image = models.ImageField(upload_to='images/')
product_type = models.CharField(max_length=100, choices=PRODUCT_TYPE_CHOICES)
def __str__(self):
return self.name
The Product class is created. Now what? You will probably run the python manage.py makemigrations
and the python manage.py migrate
command to apply the changes in the database.
The Product
table will be created but we also want to have the Shoe
and the Computer
table data in this new table so we can get rid of them.
This will require writing custom migrations.
Supposing that we have created a separated Django application called product
in the Django project, we can use the following command to generate a new empty migrations file in the product
application.
python manage.py makemigrations product --empty
We'll have a file similar to this.
# Generated by Django 4.0.2 on 2022-04-03 10:36
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('product', '0001_initial'),
]
operations = [
]
Great! We need now to import the Shoe
and Computer
models and write a function to migrate Shoe
and Computer
table data to Product
table.
def migrate_to_product_model(apps, schema_editor):
Product = apps.get_model('product', 'Product')
Shoes = apps.get_model('shoe', 'Shoes')
Computer = apps.get_model('computer', 'Computer')
for shoe in Shoes.objects.all():
Product.objects.create(
name=shoe.name,
brand=shoe.brand,
color=shoe.color,
in_stock=shoe.in_stock,
description=shoe.description,
image=shoe.image,
product_type="shoe"
)
for computer in Computer.objects.all():
Product.objects.create(
name=computer.name,
brand=computer.brand,
color=computer.color,
in_stock=computer.in_stock,
description=computer.description,
image=computer.image,
product_type="computer"
)
And here's the final migration code.
# Generated by Django 4.0.2 on 2022-04-03 10:36
from django.db import migrations
def migrate_to_product_model(apps, schema_editor):
Product = apps.get_model('product', 'Product')
Shoes = apps.get_model('shoe', 'Shoes')
Computer = apps.get_model('computer', 'Computer')
for shoe in Shoes.objects.all():
Product.objects.create(
name=shoe.name,
brand=shoe.brand,
color=shoe.color,
in_stock=shoe.in_stock,
description=shoe.description,
image=shoe.image,
product_type="shoe"
)
for computer in Computer.objects.all():
Product.objects.create(
name=computer.name,
brand=computer.brand,
color=computer.color,
in_stock=computer.in_stock,
description=computer.description,
image=computer.image,
product_type="computer"
)
class Migration(migrations.Migration):
dependencies = [
('product', '0001_initial'),
]
operations = [
migrations.RunPython(migrate_to_product_model)
]
This is an example of a custom migration. No need to worry about if the migrations fail and the database is messed up. Migrations transactions are atomic by default, then if something goes south, the changes won't be committed to the database.
π
For more reading, check the official documentation of migrations in Django.
Article posted using bloggu.io. Try it for free.
Top comments (5)
Really good article. Me too I diddn't know about it for a long time.
You might add the precision that it's a data migration (It's even called like that in the docs). The fact is, for beginners who never faced the needs to do a data migration, most of the times when we (as I was in the case) hear about migration we just think about changing database schema, creating table, adding or removing new fields, not changing the data that exists in the database.
I love this insight. Thank you Horace
Cool
Ty @sm0ke π
Great Python article.