django中的复合主键

时间:2015-02-25 06:45:39

标签: python django postgresql

我有一个具有复合主键的旧数据库表。我不认为我能够更改结构以包含代理键,因为有一些代码使用该表编写。在django中,我不能使用该表,因为它没有主键(非复合)。

django模型是否支持复合主键?如果没有,是否有任何解决方法而不改变表的结构?

P.S。我正在使用postgresql。

4 个答案:

答案 0 :(得分:41)

尝试类似下面的代码:

class MyTable(models.Model):
    class Meta:
        unique_together = (('key1', 'key2'),)

    key1 = models.IntegerField(primary_key=True)
    key2 = models.IntegerField()

或者如果您只想要唯一的混合字段:

class MyTable(models.Model):
    class Meta:
        unique_together = (('key1', 'key2'),)

    key1 = models.IntegerField()
    key2 = models.IntegerField()

编辑:我想注意,如果有3列,这种方法存在问题。更新查询无法正常工作,因为它尝试更新(在#34; SET&#34之后立即放置pk字段;)这些字段是唯一的并且显然会失败。

答案 1 :(得分:5)

solved 使用从 django AutoField 继承的虚拟字段,它将来自多个字段的值组合到单个 JSON 字典中。

这使得此类模型与 django 管理和遗传视图兼容。

$ pip install django-viewflow --pre

from viewflow.fields import CompositeKey

class Seat(models.Model):
    id = CompositeKey(columns=['aircraft_code', 'seat_no'])
    aircraft_code = models.ForeignKey(
        Aircraft, models.DO_NOTHING,
        db_column='aircraft_code'
    )
    seat_no = models.CharField(max_length=4)

这使得访问 legacy databasesPostgreSQL TimeScaleDB 表成为可能

答案 2 :(得分:1)

另一种选择是在模型的managed=False中设置Meta,然后手动创建表。

class MyTable(models.Model):
    foo = models.IntegerField(primary_key=True)
    bar = models.IntegerField()
    baz = models.IntegerField()

    class Meta:
        managed = False
        db_table = 'myapp_mytable'

    def __repr__(self):
        return f'<MyTable: MyTable object ({self.foo}, {self.bar}, {self.baz)>'

在postgres shell中:

CREATE TABLE myapp_mytable (
    foo INTEGER NOT NULL,
    bar INTEGER NOT NULL,
    baz INTEGER NOT NULL,
    PRIMARY KEY(foo, bar, baz)
);

它似乎行为正确:

>>> MyTable.objects.create(foo=1, bar=1, baz=1)
<MyTable: MyTable object (1, 1, 1)>

>>> MyTable.objects.create(foo=1, bar=1, baz=2)
<MyTable: MyTable object (1, 1, 2)>

>>> MyTable.objects.create(foo=1, bar=1, baz=2)
django.db.utils.IntegrityError: duplicate key value violates unique constraint "myapp_mytable_pkey"
DETAIL:  Key (foo, bar, baz)=(1, 1, 2) already exists.

请注意,该功能仅在Django 3.x中进行过测试,因此我不确定它是否适用于旧版本。

答案 3 :(得分:1)

接受的答案很好。但是,它有点旧了。可以不赞成使用unique_together,而赞成UniqueConstraint。因此,更好的方法是;

UniqueConstraint(fields = ['key1', 'key2'], name = 'constraint_name')