首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从多表继承模型迁移到Django中的抽象基类

从多表继承模型迁移到Django中的抽象基类
EN

Stack Overflow用户
提问于 2020-12-18 18:34:34
回答 1查看 292关注 0票数 3

我当前的项目使用multi-table inheritance models

代码语言:javascript
复制
from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

class Cinema(Place):
    sells_tickets = models.BooleanField(default=False)
    sells_popcorn = models.BooleanField(default=False)

我想换成abstract base classes。由于我的模型已经部署,我需要编写一些自定义迁移来将上面的模式转换为这个模式:

代码语言:javascript
复制
from django.db import models

class AbstractPlace(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

    class Meta:
        abstract = True

class Restaurant(AbstractPlace):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

class Cinema(AbstractPlace):
    sells_tickets = models.BooleanField(default=False)
    sells_popcorn = models.BooleanField(default=False)

有人对实现这一目标的步骤有什么建议吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-12-20 08:33:23

我最近解决了这个问题,我通过在下面的代码块中编写和运行迁移解决了这个问题-松散地转换为适合您的情况的模型。

我非常确定不可能直接更改旧的RestaurantCinema模型的表,因为如果您尝试向它们添加字段,它们将与基本模型的现有字段发生冲突,并且如果您试图通过手动设置基本模型的options中的abstract=True来“解耦”派生模型和基本模型,Django报告说它无法找到RestaurantCinema的基本模型。(据我所知,这些问题可能是由bug引起的。)为了避免这个问题,我为派生模型创建了新的表,将数据从旧的表复制到新的表中,删除旧的表,并重命名新的表以匹配旧的表的名称。

我从Django生成的代码中获得了下面的大部分代码,可以通过创建一个临时迁移(在使用下面的代码创建一个迁移之前),运行makemigrations,并从生成的迁移中复制CreateModel()AlterField()(用于指向RestaurantCinema的相关字段),来复制这些代码。

根据记录,我使用的是Django 3.1.4。

代码语言:javascript
复制
from django.db import migrations, models


def copy_objects_from_restaurant_and_cinema_to_restaurant_tmp_and_cinema_tmp(apps, schema_editor):
    Restaurant_Tmp = apps.get_model('<app name>', 'Restaurant_Tmp')
    Cinema_Tmp = apps.get_model('<app name>', 'Cinema_Tmp')
    Restaurant = apps.get_model('<app name>', 'Restaurant')
    Cinema = apps.get_model('<app name>', 'Cinema')
    # The `_meta.fields` list includes the PK
    copy_objects_from_old_model_to_new_model(Restaurant, Restaurant_Tmp, Restaurant_Tmp._meta.fields)
    copy_objects_from_old_model_to_new_model(Cinema, Cinema_Tmp, Cinema_Tmp._meta.fields)


def copy_objects_from_old_model_to_new_model(old_model, new_model, fields_to_copy):
    field_names = [field.name for field in fields_to_copy]
    for old_obj in old_model.objects.all():
        old_obj_field_dict = {
            field_name: getattr(old_obj, field_name)
            for field_name in field_names
        }
        new_model.objects.create(**old_obj_field_dict)


def copy_objects_from_restaurant_tmp_and_cinema_tmp_to_restaurant_and_cinema(apps, schema_editor):
    Restaurant_Tmp = apps.get_model('<app name>', 'Restaurant_Tmp')
    Cinema_Tmp = apps.get_model('<app name>', 'Cinema_Tmp')
    Restaurant = apps.get_model('<app name>', 'Restaurant')
    Cinema = apps.get_model('<app name>', 'Cinema')
    copy_objects_from_old_model_to_new_model(Restaurant_Tmp, Restaurant, Restaurant_Tmp._meta.fields)
    copy_objects_from_old_model_to_new_model(Cinema_Tmp, Cinema, Cinema_Tmp._meta.fields)


class Migration(migrations.Migration):

    dependencies = [
        ('<app name>', '<last migration>'),
    ]

    operations = [
        migrations.CreateModel(
            name='Restaurant_Tmp',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=50)),
                ('address', models.CharField(max_length=80)),
                ('serves_hot_dogs', models.BooleanField(default=False)),
                ('serves_pizza', models.BooleanField(default=False)),
            ],
            options={
                'abstract': False,
            },
        ),
        migrations.CreateModel(
            name='Cinema_Tmp',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=50)),
                ('address', models.CharField(max_length=80)),
                ('sells_tickets', models.BooleanField(default=False)),
                ('sells_popcorn', models.BooleanField(default=False)),
            ],
            options={
                'abstract': False,
            },
        ),
        migrations.RunPython(copy_objects_from_restaurant_and_cinema_to_restaurant_tmp_and_cinema_tmp, migrations.RunPython.noop),

        # Update foreign keys to reference the non-abstract models directly,
        # instead of through the (automatically generated) `place_ptr` field of the old models
        < 
          Run `migrations.AlterField()` here for each related field (like ForeignKey) of other models that point to Restaurant or Cinema,
          but change their `to` argument from e.g. `<app name>.restaurant` to `<app name>.restaurant_tmp`
        >
        migrations.RunPython(migrations.RunPython.noop, copy_objects_from_restaurant_tmp_and_cinema_tmp_to_restaurant_and_cinema),

        migrations.DeleteModel(
            name='Restaurant',
        ),
        migrations.DeleteModel(
            name='Cinema',
        ),
        migrations.DeleteModel(
            name='Place',
        ),
        migrations.RenameModel(
            old_name='Restaurant_Tmp',
            new_name='Restaurant',
        ),
        migrations.RenameModel(
            old_name='Cinema_Tmp',
            new_name='Cinema',
        ),
    ]

请注意,我最初编写的迁移只在使用SQLite的情况下进行了测试;其他数据库管理系统可能无法接受如此多的迁移操作,您可能必须将其拆分成多个迁移。(我有点不确定到底是什么导致了这个问题,但我记得我在使用PostgreSQL时就遇到过这种情况。)

如果这解决了你的问题,请让我知道!?

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65355459

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档