假设我有一个名为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.id
和Cat.feline
的值应该相同(以便所有到Cat的ForeignKeys仍然有效)
新Cat或Feline对象应在最大的Cat ID之后分配ID,否则您最终会尝试将已使用的ID分配给新的Cat或猫。
不应删除任何依赖于Cat.id
的数据库视图(因此您无法删除Cat.id
,因为这会导致级联删除这些视图)
过去几天我已经解决了这个问题以及上述问题,我有一个解决方案,其中涉及(除其他事项外)将Cat.id
重命名为Cat.feline_id
,我将在下面给出答案 - 欢迎任何其他替代方案。
答案 0 :(得分:1)
我的解决方案如下工作。首先,添加猫科动物模型,但保持猫模型不变:
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
)。在数据迁移中,添加代码以为每个Cat创建一个猫科动物,以便创建的每个猫科动物与猫共享一个ID。此外,更改新Felines的序列,以便为所有新的Felines分配大于当前最大Cat 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()
接下来,创建另一个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))
最后,运行您刚刚编辑过的架构移民,然后就完成了。
现在,如果您愿意,可以使用进一步的模式和数据源轻松地将某些字段(例如名称)从Cat移动到Feline。
如果你想为多个现有模型创建一个超类,那么另一个挑战(幸运的是我没有必要处理) - 在这种情况下,你可能无法为所有实例保留相同的ID,如两个不同子类中的某些实例可能会在ID上发生冲突。关于如何解决这个问题的任何想法都会受到欢迎。