如果已经有相关的类,如何将django模型转换为抽象模型

时间:2015-11-15 16:21:16

标签: python django django-models abstract-class django-migrations

假设我有以下基本型号:

class human(models.Model):
   gender = models.BooleanField()
   age = models.IntegerField()
   name = models.CharField(max_length=200)

继承它的两个模型:

class superhero(human):
   can_fly = models.BooleanField()

class villain(human):
   fingerprint = models.ImageField()

在我的开发过程中的某个时候,我意识到我并不直接需要人类。我只需要它为超级英雄和恶棍模型的一组模板参数。如果现在我去人类Meta课程并设置abstract=True并改变我的模型:

class human(models.Model):
   gender = models.BooleanField()
   age = models.IntegerField()
   name = models.CharField(max_length=200)

   class Meta:
       abstract = True

class superhero(human):
   can_fly = models.BooleanField()

class villain(human):
   fingerprint = models.ImageField()

尝试进行迁移和迁移会引发以下错误

  课堂上的“超级英雄”中的“本地字段u'gender”与基类“人类”中名字相似的字段发生冲突

如何在不修改数据库的情况下切换到抽象类来保留所有迁移?

2 个答案:

答案 0 :(得分:3)

因此,在再次阅读文档后,我找到了一个解决方案:

由于Django将模型保存到数据库的方式引发了错误。从基础模型human继承的所有模型在其自己的表中都没有所有human个字段。相反,它们只有自己的字段和外键,它们将它们链接到human表中的相应行。但是当你从抽象类继承时,所有字段都直接保存到模型的表中。因此,当我尝试将human类更改为abstract=True并在superhero类中继承它时,Django尝试从human表中的superhero表创建所有字段,仍然有一个外键到现有的人类条目,字段名称完全相同。

警告

按照此说明生成所需结果,但遗憾的是会销毁human superherovillain型号的所有条目

  1. 评论superherovillain模型,以便Django删除它们
  2. 进行迁移和迁移,以便删除superherovillain
  3. abstract=True班级
  4. 中设置human
  5. 进行迁移并再次迁移。这将删除human表,因为它现在是一个抽象类
  6. 取消注释superherovillain模型
  7. 进行迁移和迁移。这将创建包含villain class
  8. 中所有字段的superherohuman

    就是这样。

    P.S。为什么我需要转向抽象类?因为我想使用villains参数使我的所有superheroesunique_together唯一,这会产生一些数据库级限制。为了实现这一点,所有superhero字段都必须在一个表中。现在它有效。

答案 1 :(得分:0)

这是一个老问题,但答案为我付出了很多努力。我只是想补充一些东西。

当使模型类从抽象模型继承时,Django迁移将删除该模型。

MyModel(models.Model):
    # some fields to be inherited later from the abstract model
    author = models.ForeignKey('auth.User')
    # other fields specific to this model

现在,如果您创建一个抽象模型:

MyAbstractModel(models.Model):
    # fields to be used by children classes

    class Meta:
        abstract = True

让你的模型继承它:

MyModel(MyAbstractModel):
    author = models.ForeignKey('auth.User')
    # other fields specific to this model

如果您运行makemigrations并在您的应用程序上迁移,Django将删除该模型并删除相应的数据库表。

你可以通过评论该模型的代码来超越Django。 Django将删除所有其他后果。现在您可以取消注释代码并再次运行迁移,Django将再次创建数据库表。

但是,您可能需要管理员,视图,表单等来导入模型。这意味着迁移会抛出错误,因为无法找到该类。您必须在导入模型的所有文件中注释掉代码。

更容易跳过评论并手动编写想要的迁移:

from __future__ import unicode_literals

from django.db import migrations


class Migration(migrations.Migration):

    dependencies = [
        # app_label should be your app
        # and 000x_the_last_migration is the name of the migration
        # it depends on, usually the last one
        ('app_label', '000x_the_last_migration'),
    ]

operations = [
    # do this for all ForeignKey, ManyToManyField, OneToOneField
    # where model_name is obviously the model name and name is the
    # field name
    migrations.RemoveField(
        model_name='mymodel',
        name='author',
    ),
    # finally delete the model
    migrations.DeleteModel(
        name='MyModel',
    ),
]

现在您可以运行迁移:

python manage.py migrate app_label

稍后从抽象模型继承(参见上面的代码,第3块)并进行新的迁移:

python manage.py makemigrations app_label

这可以节省您评论大量代码。如果要将数据保存在数据库表中,则可以使用dumpdataloaddata

TLDR

这不是步骤指南的一个步骤,需要使用Django的经验。简而言之,你必须:

  1. 创建抽象模型
  2. 手动为必须从抽象模型继承的模型编写迁移
  3. 运行迁移(这将删除包含所有条目的数据库表!)
  4. 从抽象模型继承
  5. 运行makemigrations并迁移