2014-01-22 33 views
2
創建一個超類(對於現有的模型)

比方說,我有一個Django應用程序稱爲animals,其中包含已在數據庫中填充了行的模型叫貓:如何使用Django和南方

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.id

  • Cat.idCat.feline應該是相同的,對於所有Cat對象(以便所有ForeignKeys到Cat仍然有效)

  • 新的Cat或Cat對象應該在最大的Cat ID之後分配ID,否則最終會嘗試將已使用的ID分配給新的Cat或Cat。

  • 依賴Cat.id任何數據庫的觀點不應該被刪除(所以你不能刪除Cat.id,因爲那樣會導致一個級聯刪除這些意見)

關於這個問題,上述問題已經工作在過去的幾天裏,我有一個解決方案,涉及(其中包括)將Cat.id更名爲Cat.feline_id,我將在下面給出答案 - 歡迎任何其他替代方案。

+0

我想我會轉儲數據庫,使模型更改,遷移,然後恢復(我去的時候添加cat_id到貓表)。但那只是我。我認爲這需要一個小時,而不是幾天。 –

+0

當然,但如果你希望你的遷移能夠在數據庫的不同實例(例如dev,live)上可重複地前後移動,那麼這並沒有什麼用處 – jcdude

回答

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模型添加到數據庫中。

接下來,創建一個datamigration,(manage.py datamigration animals cat_feline)。在數據遷移中,添加代碼爲每個Cat創建一個Feline,以便每個創建的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 

接下來,更改模型文件,以使貓從貓繼承和OneToOneField添加到目錄,這將是對貓新的主鍵:

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。

如果您想爲多個現有模型創建一個超類,那麼幸運的是我不需要處理更多的挑戰 - 在這種情況下,您可能無法爲所有實例保留相同的ID,因爲兩個不同子類中的一些實例可能會在ID上發生衝突。任何關於如何解決這個問題的想法都是值得歡迎的。

+0

我想添加一些鏈接給那些幫助過一路上。 將OneToOne字段添加到現有模型中:http://stackoverflow.com/questions/8586703/how-to-add-one-to-one-relation-field-in-a-existing-model-through-south-migration 更改主鍵序列中的下一個ID: https://www.vlent.nl/weblog/2011/05/06/integrityerror-duplicate-key-value-violates-unique-constraint/ – jcdude

+0

另外: 更改AutoField轉換爲類似ForeignKey的字段,並返回: http://south.aeracode.org/ticket/407 爲多個現有類(但不共享ID)創建超類: http:// stackoverflow。 COM /問題/ 1600129 /使用南到重構-A-Django的模型與繼承 – jcdude