Repositório utilizado para estudar migração de dados utilizando o Django
Aqui fiz um app, onde inicialmente eu tinha uma model Book
, e nessa mesma model ficavam as seguintes informações sobre livros:
- Id
- Título
- Páginas
- Autor
Nessa situação fictícia, eu preciso separar os autores em uma model separada, e utilizarei as migrations do Django para isso.
Ao final do processo, a model Book
deve conter os campos:
- Id
- Título
- Páginas
- Autor (ForeignKey)
E a model Author
deve conter:
- Id
- Nome
Nessa simulação, a model Book
foi inicialmente declarada assim:
class Book(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
title = models.CharField(max_length=255)
pages = models.IntegerField()
author = models.CharField(max_length=255)
O primeiro passo, foi criar a model Author
:
class Author(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
name = models.CharField(max_length=255)
- Aqui eu gerei uma migration
Segundo passo, adicionar na model Book
, o campo do tipo ForeignKey que fará o vínculo do livro com o autor. Importante manter inicialmente este campo com default=None
e null=True,
:
class Book(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
title = models.CharField(max_length=255)
pages = models.IntegerField()
author = models.CharField(max_length=255)
author_foreign_key = models.ForeignKey(
Author,
on_delete=models.CASCADE,
default=None,
null=True,
blank=True,
)
- Aqui eu gerei uma migration
Agora o momento principal: Eu crio uma migration vazia, com o comando:
python manage.py makemigrations core --empty
Isso nos da uma migration vazia com o seguinte templade:
# Generated by Django 4.0.1 on 2022-01-06 00:09
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0003_book_author_foreign_key'),
]
operations = [
]
Renomeei essa migration para 0004_separate_authors_from_books.py
É aqui que vou escrever a lógica da migração dos dados
Talvez você precise alterar o "dono" do arquivo para poder editá-lo, caso seja necessário, utilize o comando:
sudo chown $USER 0004_separate_authors_from_books.py
A migration com a lógica completa de separação dos dados ficou assim:
from django.db import migrations
class Migration(migrations.Migration):
def create_authors_from_books(apps, schema_editor):
model_book = apps.get_model("core", "Book")
model_author = apps.get_model("core", "Author")
books = model_book.objects.all()
for book in books:
author, _ = model_author.objects.get_or_create(
name=book.author
)
book.author_foreign_key = author
book.save()
def reverse_create_authors_from_books(apps, schema_editor):
model_book = apps.get_model("core", "Book")
model_author = apps.get_model("core", "Author")
books = model_book.objects.all()
for book in books:
book.author_foreign_key = None
book.save()
model_author.objects.all().delete()
dependencies = [
('core', '0003_book_author_foreign_key'),
]
operations = [
migrations.RunPython(
create_authors_from_books, reverse_create_authors_from_books
)
]
TODO: Explicar melhor os métodos criados aqui.
- Aqui eu executei a migration criada acima
Com os dados migrados, eu removo o campo author
da model Book
- Aqui eu gerei uma migration
Por último, eu renomeio o field author_foreign_key
para um nome mais descente, no caso, author
mesmo.
- Aqui eu gerei uma migration