Python 如何使用Django和South创建超类(针对现有模型)

Python 如何使用Django和South创建超类(针对现有模型),python,django,inheritance,django-south,Python,Django,Inheritance,Django South,假设我有一个Django应用程序,名为动物,其中包含一个名为Cat的模型,该模型已经在数据库中填充了行: class Cat(models.Model): objects = models.Manager() name = models.CharField(max_length=255) miaow_factor = models.IntegerField() 创建Cat超类(如猫)并使用南方迁移将其添加到数据库中的最佳方法是什么?i、 e.最后,我想介绍一下结构:

假设我有一个Django应用程序,名为
动物
,其中包含一个名为Cat的模型,该模型已经在数据库中填充了行:

class Cat(models.Model):
    objects = models.Manager() 

    name = models.CharField(max_length=255)
    miaow_factor = models.IntegerField()
创建Cat超类(如猫)并使用南方迁移将其添加到数据库中的最佳方法是什么?i、 e.最后,我想介绍一下结构:

class Feline(models.Model):
    objects = models.Manager() 

class Cat(Feline):
    #n.b. Cat now inherits from Feline, not models.Model
    objects = models.Manager() 

    feline = models.OneToOneField(Feline, parent_link = True)
    name = models.CharField(max_length=255)
    miaow_factor = models.IntegerField()
请记住:

  • Cat.feline
    应该是Cat的新主键,取代
    Cat.id

  • 对于所有Cat对象,
    Cat.id
    Cat.feline
    的值应相同(以便Cat的所有外键保持有效)

  • 新的猫或猫科动物对象应该在最大的猫ID之后分配ID,否则最终您将尝试将已使用的ID分配给新的猫或猫科动物

  • 不应删除依赖于
    Cat.id
    的任何数据库视图(因此不能删除
    Cat.id
    ,因为这将导致级联删除这些视图)


在过去几天里,我一直在研究这个问题和上述问题,我有一个解决方案,其中包括将
Cat.id
重命名为
Cat.feline_id
,我将在下面给出答案-欢迎任何其他选择。

我的解决方案如下所示。首先,添加猫科动物模型,但保持猫科动物模型不变:

class Feline(models.Model):
    objects = models.Manager() 

class Cat(models.Model):
    objects = models.Manager() 

    name = models.CharField(max_length=255)
    miaow_factor = models.IntegerField()
接下来,创建并运行schemamigration(
manage.py schemamigration animals--auto
),以便将
Feline
模型添加到数据库中

接下来,创建一个数据迁移(
manage.py datamigration anies cat_猫
)。在数据迁移中,添加代码为每只猫创建一只猫,这样创建的每只猫都与一只猫共享一个ID。此外,更改新猫科动物的顺序,以便为所有新猫科动物分配大于当前最大猫科动物ID的ID

class Migration(DataMigration):    
    def forwards(self, orm):
        #create a Feline for each Cat
        for c in orm['Cat'].objects.all():
            f = orm['Feline']()
            f.id = c.id
            f.save()

        if orm['Feline'].objects.count():
            #if there are any Feline objects, make sure that new ids are allocated after the largest current ID
            last_id = orm['Feline'].objects.latest('id').id
            db.execute('alter sequence animals_feline_id_seq restart with %s;' % (last_id + 1)) 


    def backwards(self, orm):
        #no need to do anything if migrating backwards
        pass 
接下来,更改模型文件以使Cat从猫继承,并将OneToOneField添加到Cat,这将是Cat的新主键:

class Feline(models.Model):
    objects = models.Manager() 

class Cat(Feline):
    #n.b. Cat now inherits from Feline, not models.Model
    objects = models.Manager() 

    feline = models.OneToOneField(Feline, parent_link = True)
    name = models.CharField(max_length=255)
    miaow_factor = models.IntegerField()
接下来,创建另一个schemamigration,以便将这些更改应用于数据库。但是,不要运行迁移。相反,将迁移中的代码更改为将
Cat.id
列重命名为
Cat.猫科动物id

class Migration(SchemaMigration):

    def forwards(self, orm):
        #original changes generated by South:
        # Deleting field 'Cat.id'
        #db.delete_column(u'animals_cat', u'id')

        # Adding field 'Cat.feline'
        #db.add_column(u'animals_cat', 'feline',
        #              self.gf('django.db.models.fields.related.OneToOneField')(default=None, to=orm['animals.Feline'], unique=True, primary_key=True),
        #              keep_default=False)

        #instead of doing the above, just rename Cat.id to Cat.feline_id
        #and drop the default numbering sequence for the Cat.feline_id field 
        db.rename_column('animals_cat', 'id', 'feline_id')
        db.execute("ALTER TABLE animals_cat ALTER COLUMN feline_id DROP DEFAULT")



    def backwards(self, orm):
        #original changes generated by South:
        # Adding field 'Cat.id'
        #db.add_column('animals_cat', u'id',
        #              self.gf('django.db.models.fields.AutoField')(default=None, primary_key=True),
        #              keep_default=False)

        # Deleting field 'Cat.feline_id'
        #db.delete_column(u'animals_cat', 'feline_id')

        #instead of doing the above, rename Cat.feline_id to Cat.id
        #and reinstate the default numbering sequence for the Cat.id field
        db.rename_column('animals_cat', 'feline_id', 'id')
        db.execute("ALTER TABLE animals_cat ALTER COLUMN id SET DEFAULT nextval('u'animals_cat_id_seq'::regclass)")

        if orm['Cat'].objects.count():
            #if there are any Cat objects, make sure that new ids are allocated after the largest current ID
            last_id = orm['Cat'].objects.latest('id').id
            db.execute('alter sequence animals_cat_id_seq restart with %s;' % (last_id + 1)) 
最后,运行您刚刚编辑的schemamigration,就完成了

现在,如果您愿意,您可以使用进一步的模式转换和数据迁移,轻松地将一些字段(例如name)从Cat移动到Cat


另一个挑战(幸运的是,我没有处理)是,如果您想为多个现有模型创建一个超类-在这种情况下,您可能无法为所有实例保留相同的ID,由于两个不同子类中的某些实例可能会在ID上发生冲突。任何关于如何解决这一问题的想法都是受欢迎的。

我想添加一些链接,指向在此过程中有所帮助的源代码。将OneToOne字段添加到现有模型:更改主键序列中的下一个ID:另外:将AutoField更改为ForeignKey-like字段,然后返回:为多个现有类(但没有共享ID)创建超类:我想我应该转储db,进行模型更改,迁移,然后恢复(在我进行迁移时将cat_id添加到猫表中)。但这只是我自己的问题。我认为这需要一个小时,而不是几天。当然,但如果您希望迁移能够在数据库的不同实例(例如dev、live)上重复地向前和向后移动,那么这并没有多大用处