【问题标题】:How to create a superclass (for an existing model) with Django and South如何使用 Django 和 South 创建超类(用于现有模型)
【发布时间】:2014-02-12 22:54:32
【问题描述】:

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

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

    name = models.CharField(max_length=255)
    miaow_factor = models.IntegerField()

创建 Cat 的超类(例如 Feline)并使用 South 迁移将其添加到数据库的最佳方法是什么?即我想以结构结束:

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.idCat.feline 的值应该相同(以便 Cat 的所有 ForeignKeys 保持有效)

  • 新的 Cat 或 Feline 对象的 ID 应在最大的 Cat ID 之后分配,否则您最终会尝试将已使用的 ID 分配给新的 Cat 或 Feline。

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

在过去几天里解决了这个问题和上述问题,我有一个解决方案,包括(除其他外)将Cat.id 重命名为Cat.feline_id,我将在下面给出答案 - 欢迎任何其他替代方案.

【问题讨论】:

  • 我想我会转储数据库,更改模型,迁移,然后恢复(将 cat_id 添加到 feline 表中)。但这只是我。我认为这需要一个小时,而不是几天。
  • 当然,但是如果您希望您的迁移能够在数据库的不同实例(例如开发、实时)上可重复地向前和向后进行,那么这并没有多大用处

标签: python django inheritance django-south


【解决方案1】:

我的解决方案如下。首先,添加 Feline 模型,但 Cat 模型保持不变:

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

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

    name = models.CharField(max_length=255)
    miaow_factor = models.IntegerField()

接下来,创建并运行架构迁移 (manage.py schemamigration animals --auto),以便将 Feline 模型添加到数据库中。

接下来,创建一个数据迁移 (manage.py datamigration animals cat_feline)。在数据迁移中,添加代码为每只猫创建一只猫,这样创建的每只猫都与一只猫共享一个 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 继承自 Feline,并将 OneToOneField 添加到 Cat,这将是 Cats 的新主键:

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.id 列重命名为Cat.feline_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)) 

最后,运行刚刚编辑的架构迁移,就大功告成了。

现在,如果您愿意,您可以使用进一步的架构迁移和数据迁移轻松地将一些字段(例如名称)从 Cat 移动到 Feline。

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

【讨论】:

猜你喜欢
  • 2012-04-06
  • 2013-09-25
  • 1970-01-01
  • 1970-01-01
  • 2011-03-15
  • 1970-01-01
  • 2010-12-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多