Skip to main content
 首页 » 编程设计

python之如何使用 Django 和 South 创建父类(super class)(针对现有模型)

2025年05月04日24dyllove98

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

class Cat(models.Model): 
    objects = models.Manager()  
 
    name = models.CharField(max_length=255) 
    miaow_factor = models.IntegerField() 

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

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 的所有 ForeignKeys 保持有效)

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

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

过去几天一直在处理这个问题和上述问题,我有一个解决方案,涉及(除其他事项外)将 Cat.id 重命名为 Cat.feline_id 我将在下面给出答案 - 欢迎任何其他选择。

请您参考如下方法:

我的解决方案如下。首先,添加 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() 

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

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

接下来,创建另一个 schemamigration,以便将这些更改应用到数据库。但是,不要运行迁移。相反,更改迁移中的代码以将 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))  

最后,运行您刚刚编辑的 schemamigration,您就完成了。

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

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