2015-04-24 66 views
6

我有一個叫Document的模型,我想添加一個新的表,DocumentCluster,它位於上面,外鍵爲Document如何設置主鍵,然後轉換爲自動字段?

class DocumentCluster(models.Model): 
    sub_document = models.ForeignKey(Document) 
    ...lots of fields here... 

當我使用South添加此表時,我需要通過將主鍵和外鍵設置爲相同的值來填充它。

例如,如果我現在有12的pk一個Document對象,新DocumentCluster對象將有12的pk和外鍵Document數12

雖然它可能看起來很奇怪,我們需要DocumentClusterpk值來匹配外鍵值還有一個重要原因。我們在我們的網址中使用Documentpk,但在更改之後,網址將加載DocumentCluster而不是Document,因此我們需要pkDocumentClusterDocument中的相同。

一旦完成,我希望DocumentCluster的PK成爲AutoField,從已遷移的最高值開始遞增。

可以這樣做嗎?

+1

爲什麼你需要設置PK?作爲一個自然的關鍵,它應該與內容無關。你有fk來確定關係。 –

+0

我們在我們的URL中使用PK,但在更改後,URL將加載「DocumentCluster」,而不是「Document」,因此我們需要將PK與以前相同。 – mlissner

回答

1

你不能有一個FK DocumentCluster.pkDocument,並在同一時間做DocumentCluster.pk一個serial列。這是一個矛盾。它必須是兩個單獨的列。我想你的意思是反過來:DocumentDocumentCluster

這可以用SQL DDL完成命令:

BEGIN; 

CREATE TABLE DocumentCluster (pk serial, lots text, of_fields int, ...); 

INSERT INTO DocumentCluster (pk, lots, of_fields, ...) 
SELECT pk, lots, of_fields, ... FROM Document 
ORDER BY pk; -- optional 

ALTER TABLE DocumentCluster ADD CONSTRAINT DocumentCluster_pkey 
PRIMARY KEY (pk); -- could be integrated in CREATE TABLE 

ALTER TABLE Document -- and not the other way round! 
ADD CONSTRAINT pk_fk FOREIGN KEY (pk) REFERENCES DocumentCluster(pk); 

SELECT setval(pg_get_serial_sequence('DocumentCluster', 'pk'), max(pk)) 
FROM DocumentCluster; -- update SEQUENCE to highest value 

COMMIT; 
  • 在這種情況下,你必須做同樣的你的混淆層(Django的)可使用諸如"Document"雙引號的名字, ...

  • About setval().

  • About pg_get_serial_sequence()

  • 可以在CREATE TABLE語句中聲明PK,但在行插入後添加它會更便宜 - 假設pkunique not null

更多的解釋和鏈接:

+0

我認爲你的SQL是正確的,但沒有任何理由mlissner在DocumentCluster模型中不能同時擁有串行pk和sub_document外鍵。而且我不確定你使用'混淆圖層'指的是什麼。如果您添加到sub_document字段/列中,SQL會更清晰。 – nmgeek

+0

@nmgeek:就像我寫的:它必須是兩個單獨的列 - 就像你所建議的那樣。理論上,參考列也可以是「串行」。但從一個序列中繪製數值是毫無意義的,而它們必須是對另一個表中的鍵的引用。我澄清了一下。我沒有添加'sub_document'字段,因爲在我顛倒FK的方向後我不認爲它是必需的。 –

+0

正如我在原始問題中所說的那樣,我真的希望儘可能地在南方做到這一點,這樣我就可以獲得它帶來的所有好處。如果有辦法,我可以使用South遷移數據,然後將pk更新爲序列號,這將是我的首選解決方案。 – mlissner

1

使用南這比我想象的要簡單得多。

像往常一樣使用南schemamigration命令來構建添加DocumentCluster模型的模式遷移。

然後使用datamigration命令建立一個骨架爲下遷移:

./manage.py datamigration yourapp populate_clusters 

然後在向前方法填補在所得蟒遷移文件所以它看起來像:

def forwards(self, orm): 
    max_id = -1 
    clusters_added = 0                           
    for document in orm.Document.objects.all(): 
     cluster = orm.DocumentCluster() 
     cluster.id = document.id 
     cluster.sub_document = document 
     cluster.save() 
     max_id = max(max_id, document.id) 
     clusters_added +=1 
    if max_id >= clusters_added: 
     orm.DocumentCluster.objects.raw("SELECT "\ 
      "setval(pg_get_serial_sequence('yourapp_documentcluster',"\ 
             "'id'), %d)" % max_id+1) 

(該數據遷移中的反向方法將簡單地刪除所有DocumentModel實例。)

如果在添加DocumentCluster表的遷移步驟之後立即運行此'populate_clusters'遷移步驟,那麼Doc​​umentCluster表爲空,並且serial/autokey計數器從零開始。如果您從未刪除任何文檔,那麼串行/自動密鑰計數器值將最終以下一個未使用的值爲pk值,並且您甚至不必像Erwin的答案中所示那樣將其碰撞。

但是,如果你已經刪除文檔情況下,或者以某種方式的ID值被跳過,那麼你就需要顛簸起來,使用SQL串口/自動密鑰計數器。 Django提供了一種直接執行原始SQL的方法。 (我不認爲你可以不使用原始SQL)。

爲了安全起見,您應該檢查cluster.id是否爲< =循環中顯示的最大document.id值。

+0

對於我的目的來說,這看起來好得多。我很驚訝,Django會讓你手動設置一個主鍵的值,但如果只需要這些,那麼我們就是在做生意! – mlissner

+0

在處理別的事情時,我發現Django實際上有手動設置自動主鍵的文檔。 https://docs.djangoproject.com/en/1.8/ref/models/instances/#explicitly-specifying-auto-primary-key-values – mlissner

相關問題