我有一个具有复合主键的旧数据库表。我不认为我能够更改结构以包含代理键,因为有一些代码使用该表编写。在django中,我不能使用该表,因为它没有主键(非复合)。
django模型是否支持复合主键?如果没有,是否有任何解决方法而不改变表的结构?
P.S。我正在使用postgresql。
答案 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 databases、PostgreSQL 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')