如何设置主键,然后转换为autofield?

时间:2015-04-24 18:53:12

标签: django postgresql django-south

我有一个名为Document的模型,我想添加一个位于其上方的新表DocumentCluster,并带有Document的外键。

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

当我使用South添加此表时,我需要通过将主键和外键设置为相同的值来填充它。

例如,如果我当前有一个Document对象,pk为12,则新DocumentCluster对象的pk为12,外键为Document号码12。

虽然我们需要DocumentCluster pk值来匹配外键值似乎很奇怪,但有一个重要原因。我们在网址中使用了Document pk,但在更改后,网址将加载DocumentCluster,而不是Document,因此我们需要pk } DocumentCluster中的内容与Document中的相同。

完成后,我希望DocumentCluster的PK为自动字段,从已迁移的最高值开始递增。

可以这样做吗?

2 个答案:

答案 0 :(得分:1)

你不能从 DocumentCluster.pk Document拥有FK ,同时让DocumentCluster.pk成为{{3} }列。这是一个矛盾。它必须是两个单独的列。我想你的意思是反过来: Document DocumentCluster

这可以使用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",在这种情况下您必须执行相同的操作...

  • serial

  • About setval().

  • PK可以在CREATE TABLE语句中声明,但在插入行后添加它会更便宜 - 假设pkunique not null

    < / LI>

更多解释和链接:

答案 1 :(得分:1)

使用南方这比我想象的要简单得多。

像往常一样使用south schemamigration 命令来构建添加DocumentCluster模型的架构迁移。

然后使用 datamigration 命令为下一次迁移构建骨架:

./manage.py datamigration yourapp populate_clusters

然后在生成的python迁移文件中填写 forwards 方法,如下所示:

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)

(此数据迁移中的 reverse 方法只会删除所有DocumentModel实例。)

如果这个&#39; populate_clusters&#39;迁移步骤在添加DocumentCluster表的迁移步骤之后立即运行,然后DocumentCluster表为空,并且serial / autokey计数器从零开始。如果您从未删除任何文档,则序列/自动键计数器值将以 Document pk值的下一个未使用值结束,您甚至不必碰撞如Erwin的答案所示。

但是,如果您删除了文档实例,或者如果某个 id 值被跳过,那么您需要使用这个序列/自动键计数器来提升SQL。 Django提供了一种直接执行原始SQL的方法。 (我不认为你可以不使用原始SQL)。

为安全起见,您应该检查cluster.id是否是&lt; =循环中看到的最大document.id值。