1

Convertir un modelo simple a un multi-table inheritance (Hereda de otro modelo)

Si como yo te ha pasado que después de un largo tiempo de desarrollo de un sistema, te das cuenta que tus modelos pueden tener una mejor organización utilizando herencia de modelos los que ayuda a tener un código mas simple y ordenado ademas de evitar duplicar código en los modelos.

Por poner un ejemplo supongamos que tenemos tres modelos en una aplicación llamada schools (School, Student y Class)

from django.db import models

classSchool(models.Model):
    name = models.CharField(max_length=20)

    def__str__(self):return'%s' % (self.name)


classStudent(models.Model):
    name = models.CharField(max_length=20)
    school = models.ForeignKey(
        School, null=True, blank=True, on_delete=models.CASCADE
    )

    def__str__(self):return'%s' % (self.name)


classClass(models.Model):
    name = models.CharField(max_length=20)
    students = models.ManyToManyField(Student)

    def__str__(self):return'%s' % (self.name)

La aplicación tiene un año en producción (nuestros modelos tienen registros en la base de datos), por alguna razón necesitas Generar un modelo llamado People del cual heredara el modelo Student

Podríamos pensar en modificar nuestros modelos, generar migraciones y migrar de esta forma

from django.db import models

classPeople(models.Model):
    name = models.CharField(max_length=20)

    def__str__(self):return'%s' % (self.name)


classStudent(People):
    school = models.ForeignKey(
        School, null=True, blank=True, on_delete=models.CASCADE
    )

Si hacemos esto nos va a marcar este error

You are trying toadd a non-nullable field 'people_ptr'to student without a default; we can't do that (the database needs something to populate existing rows).

Como Solucionamos este problema

  1. Agregamos el nuevo modelo de People y creamos las migraciones con python manage.py makemigration
classPeople(models.Model):
    name = models.CharField(max_length=20)

    def__str__(self):return'%s' % (self.name)

Esto nos generara un archivo de migración como este

# Generated by Django 2.0.1 on 2018-10-18 23:59from django.db import migrations, models
classMigration(migrations.Migration):

    dependencies = [
        ('schools', '0003_auto_20181018_1618'),
    ]

    operations = [
        migrations.CreateModel(
            name='People',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=20)),
            ],
        ),
    ]
  1. Este lo modificamos a que quede de esta forma
# Generated by Django 2.0.1 on 2018-10-18 23:59from django.db import migrations, models

defload_info(apps, schema_editor):
    People = apps.get_model("schools", "People")
    Student = apps.get_model("schools", "Student")

    for student in Student.objects.all():
        People.objects.create(
            id=student.people_ptr,
            name=student.name,
        )


classMigration(migrations.Migration):
    atomic = False
    dependencies = [
        ('schools', '0003_auto_20181018_1618'),
    ]

    operations = [
        migrations.CreateModel(
            name='People',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=20)),
            ],
        ),
        migrations.RenameField(
            model_name='student',
            old_name='id',
            new_name='people_ptr',
        ),
        migrations.RunPython(load_info),
        migrations.RemoveField(
            model_name='student',
            name='name',
        ),
        migrations.AlterField(
            model_name='student',
            name='people_ptr',
            field=models.OneToOneField(auto_created=True, on_delete=models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='schools.People'),
        ),
    ]
  1. Modificamos nuestro modelo Student
    Quitando el campo de name y definiendo que hereda del modelo People
classStudent(People):
    school = models.ForeignKey(
        School, null=True, blank=True, on_delete=models.CASCADE
    )
  1. migramos con el comando de python manage.py migrate

Con estos paso deberíamos de tener nuestros modelos funcionando.

Espero les sea útil este aporte.

Escribe tu comentario
+ 2
1
8589Puntos

por lo que entendi lo que hiciste en la migracion fue duplicar la tabla y luego remplazarla por una nueva?

1
4818Puntos
2 años

En resumen se envía la información de la tabla anterior a la nueva tabla y se adapta a los nuevos modelos.