如何为Django和South创建超类(现有模型)?

时间:2021-01-03 23:47:47

Let's say that I have a Django app called animals, which contains a model called Cat that is already populated with rows in the database:

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

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

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

What is the best way of creating a superclass (e.g. Feline) of Cat, and adding it to the database using South migrations? i.e. I would like to end up with the structure:

创建一个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()

Bear in mind that:

记住:

  • Cat.feline should be the new primary key of Cat, replacing Cat.id

    猫。猫应该是猫的新主键,取代猫的id

  • The values of Cat.id and Cat.feline should be the same, for all Cat objects (so that all ForeignKeys to Cat remain valid)

    猫的值。id和猫。猫应该是一样的,因为所有的猫对象(所以所有的外国猫都保持有效)

  • New Cat or Feline objects should have IDs allocated after the largest Cat ID, otherwise you will eventually end up trying to allocate an already used ID to a new Cat or Feline.

    新的猫或猫对象应该在最大的猫ID之后分配ID,否则最终您将试图将一个已经使用过的ID分配给新的猫或猫。

  • Any database views that depend on Cat.id should not be deleted (so you cannot delete Cat.id, because that would cause a cascade delete of those views)

    任何依赖Cat的数据库视图。id不应该被删除(所以你不能删除猫)。id,因为这会导致视图的级联删除)

Having worked on this problem and the above issues for the last few days, I have a solution that involves (amongst other things) renaming Cat.id to Cat.feline_id that I will give as an answer below - any other alternatives welcome.

在过去的几天里,我已经解决了这个问题和上面的问题,我有了一个解决方案,其中包括重命名Cat。id的猫。我将在下文中给出答案-欢迎其他选择。

1 个解决方案

#1


1  

My solution works as follows. First, add the Feline model, but leave the Cat model unchanged:

我的解决方法如下。首先,加入猫模型,但不改变猫模型:

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

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

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

Next, create and run a schemamigration (manage.py schemamigration animals --auto), in order to add the Feline model to the database.

接下来,创建并运行schemamigration(管理)。py schemamigration animals—auto),用于向数据库中添加猫科模型。

Next, create a datamigration, (manage.py datamigration animals cat_feline). In the datamigration, add code to create a Feline for each Cat, such that each Feline that is created shares an ID with a Cat. Additionally, change the sequence for new Felines so that all new Felines are allocated IDs that are larger than the largest current Cat ID.

接下来,创建一个datamigration (manage)。py datamigration动物cat_feline)。在datamigration中,添加代码为每只猫创建一只猫,这样创建的每只猫都与一只猫共享一个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 

Next, change the models file to make Cat inherit from Feline, and add the OneToOneField to Cat which will be the new primary key for Cats:

接下来,修改模型文件,使猫从猫科动物那里继承,并将OneToOneField添加到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()

Next, create another schemamigration, in order to apply these changes to the database. However, don't run the migration. Instead, change the code in the migration to rename the Cat.id column to Cat.feline_id

接下来,创建另一个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)) 

Finally, run the schemamigration that you have just edited, and you're done.

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

Now, if you wish, you can easily move some fields (e.g. name) from Cat to Feline, using further schemamigrations and datamigrations.

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

A further challenge (that I have not had to deal with, luckily) would be if you wanted to create a superclass for multiple existing models - in that case you may not be able to keep the same ID for all instances, as some instances in the two different subclasses might * on ID. Any thoughts on how to work around this would be welcome.

进一步挑战(我没有处理,幸运的是)如果你想创建一个超类多个现有模型——在这种情况下,你可能无法保持所有实例相同的ID,某些情况下的两种不同的子类可能冲突ID。任何想法如何处理这将是受欢迎的。

#1


1  

My solution works as follows. First, add the Feline model, but leave the Cat model unchanged:

我的解决方法如下。首先,加入猫模型,但不改变猫模型:

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

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

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

Next, create and run a schemamigration (manage.py schemamigration animals --auto), in order to add the Feline model to the database.

接下来,创建并运行schemamigration(管理)。py schemamigration animals—auto),用于向数据库中添加猫科模型。

Next, create a datamigration, (manage.py datamigration animals cat_feline). In the datamigration, add code to create a Feline for each Cat, such that each Feline that is created shares an ID with a Cat. Additionally, change the sequence for new Felines so that all new Felines are allocated IDs that are larger than the largest current Cat ID.

接下来,创建一个datamigration (manage)。py datamigration动物cat_feline)。在datamigration中,添加代码为每只猫创建一只猫,这样创建的每只猫都与一只猫共享一个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 

Next, change the models file to make Cat inherit from Feline, and add the OneToOneField to Cat which will be the new primary key for Cats:

接下来,修改模型文件,使猫从猫科动物那里继承,并将OneToOneField添加到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()

Next, create another schemamigration, in order to apply these changes to the database. However, don't run the migration. Instead, change the code in the migration to rename the Cat.id column to Cat.feline_id

接下来,创建另一个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)) 

Finally, run the schemamigration that you have just edited, and you're done.

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

Now, if you wish, you can easily move some fields (e.g. name) from Cat to Feline, using further schemamigrations and datamigrations.

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

A further challenge (that I have not had to deal with, luckily) would be if you wanted to create a superclass for multiple existing models - in that case you may not be able to keep the same ID for all instances, as some instances in the two different subclasses might * on ID. Any thoughts on how to work around this would be welcome.

进一步挑战(我没有处理,幸运的是)如果你想创建一个超类多个现有模型——在这种情况下,你可能无法保持所有实例相同的ID,某些情况下的两种不同的子类可能冲突ID。任何想法如何处理这将是受欢迎的。