簡體   English   中英

從多表 inheritance model 遷移到 Django 中的抽象基類

[英]Migrate from multi-table inheritance model to abstract base classes in Django

我當前的項目使用多表 inheritance 型號

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)

我想改用抽象基類 由於我的 model 已經部署,我需要編寫一些自定義遷移來將上述模式轉換為這個模式:

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)

有人對實現這一目標的步驟有任何建議嗎?

我最近解決了這個確切的問題,我通過在下面的代碼塊中編寫和運行遷移解決了這個問題 - 松散地翻譯以適合您的案例。

我很確定直接更改舊的RestaurantCinema模型的表是不可能的,就像您嘗試向它們添加字段一樣,它們將與基礎 model 的現有字段發生沖突,如果您嘗試“將派生模型與基礎 model 解耦,例如通過在基礎模型的options中手動設置abstract=True , Django 報告它無法找到RestaurantCinema的基礎模型。 (據我所知,這些問題可能是由錯誤引起的。)為了避免這個問題,我為派生模型創建了新表,將舊表中的數據復制到新表中,刪除了舊表,並重命名了新表以匹配舊表的名稱。

我從 Django 生成的代碼中獲得了下面的大部分代碼,可以通過創建一個臨時遷移(在使用下面的代碼創建一個之前)復制它,它只刪除RestaurantCinemaPlace ,運行makemigrations並復制CreateModel() s 和AlterField() s(用於指向RestaurantCinema的相關字段)來自生成的遷移。

作為記錄,我使用的是 Django 3.1.4。

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 上經歷過。)

如果這能解決您的問題,請告訴我!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM