Django多列外键

时间:2012-10-30 06:07:09

标签: django django-models

是否可以在另一个模型中定义引用多列的外键?

例如,一个外键引用product表中的两列索引和SQL语句:

FOREIGN KEY (product_category, product_id) REFERENCES product(category, id)

顺便说一下,我已经研究过django.contrib.contenttypes并且认为这不是这种情况的完美解决方案。

4 个答案:

答案 0 :(得分:11)

目前尚不支持。如果你愿意,有一张票和可能的方法来处理它。也许你甚至可以运行custom sql

多列主键支持

关系数据库设计使用一组列作为表的主键。当此集包含多个列时,它被称为“复合”或“复合”主键。 (有关术语的更多信息,请参阅讨论数据库密钥的文章)。 目前,Django模型仅支持此集合中的单个列,从而拒绝许多设计,其中表的自然主键是多列。 Django目前无法使用这些模式;它们必须引入冗余的单列密钥(“代理”密钥),迫使应用程序在任何给定实例中对表使用哪个密钥进行任意和其他不必要的选择。 本页讨论如何让Django支持这些复合主键。这里有很多细节,但是做得恰到好处,它将为数据建模提供更多的灵活性和潜在的简单性。

当前状态

当前状态是接受/分配和处理问题,并且在http://github.com/dcramer/django-compositepks上有部分实现。该实现允许具有复合主键。但是,ForeignKey和RelatedManager中缺少对复合键的支持。因此,无法从具有复合主键的模型中导航关系。

<强>讨论

David Cramer's initial patch

The composite foreign key API design

Ticket

注意 - SqlAlchemy允许如下所述,您可以使用SqlAlchemy替换Django的ORM

也可以使用 ForeignKeyConstraint 对象在表级别定义外键。该对象可以描述单列或多列外键。多列外键称为复合外键,并且几乎总是引用具有复合主键的表。下面我们定义一个具有复合主键的表发票:

invoice = Table('invoice', metadata,
    Column('invoice_id', Integer, primary_key=True),
    Column('ref_num', Integer, primary_key=True),
    Column('description', String(60), nullable=False)
)

然后是一个表invoice_item,其中包含一个引用发票的复合外键:

invoice_item = Table('invoice_item', metadata,
    Column('item_id', Integer, primary_key=True),
    Column('item_name', String(60), nullable=False),
    Column('invoice_id', Integer, nullable=False),
    Column('ref_num', Integer, nullable=False),
    ForeignKeyConstraint(['invoice_id', 'ref_num'], ['invoice.invoice_id', 'invoice.ref_num'])
)

Reference

答案 1 :(得分:0)

是的,但是当您使用多列约束(即外键或主键)时,您将需要创建复合键。 例如:

CREATE TABLE Student (
   S_num INTEGER,
   S_Cate INTEGER,
   S_descr CHAR(200),
   PRIMARY KEY (S_num, S_Cate))

CREATE TABLE sub_Student (
   Ssub_ID INTEGER PRIMARY KEY,
   Sref_num INTEGER,
   Sref_Cate INTEGER,
   sub_descr CHAR(500),
   FOREIGN KEY (Sref_num, Sref_Cate) REFERENCES Student
      (S_num, S_Cate))

答案 2 :(得分:0)

无论如何,你可以像这样创建一个“Django fixture”:

CREATE INDEX product_category_id_id ON product (category_id, id);

为此,您必须在模型所在的子文件夹product.sql上创建名为sql的文件。夹具加载在初始syncdb上。

答案 3 :(得分:0)

@pratik-mandrekar 的回答很好,但我想指出的是,即使没有适当的多列主键; django 能够容纳跨越多列外键的查询。这是一个基于旧数据库的示例,我不允许修改其架构:

给定:

from django.db import models

class Account(models.Model):
    # Collectively, location_no and occupant_no function as the primary key for Account.
    location_no = models.IntegerField()
    occupant_no = models.SmallIntegerField()

    name = models.CharField(max_length=100)

    class Meta:
        managed = False
        db_table = 'csracct'
        unique_together = (('location_no', 'occupant_no'),)

class Call(models.Model):
    call_id = models.IntegerField(primary_key=True)
    
    # Collectively, location_no and occupant_no act as a foreign key to Account.
    location_no = models.IntegerField()
    occupant_no = models.SmallIntegerField()

    notes = models.TextField()

    class Meta:
        managed = False
        db_table = 'csrcall'

以下是您使用 extra() 获取名为“steve”的帐户的 10 个最近调用的方法:

calls = Call.extra(
    tables = ['csracct'],
    where = [
        'csracct.location_no=csrcall.location_no',
        'csracct.occupant_no=csrcall.occupant_no',
        'csracct.name=%s',
        ],
    params = ['steve'],
    ).order_by('-call_id')[:10]

这不是最优雅的解决方案,但 extra() 是 django 基本查询集工具包的一部分;所以它可以很好地与您的 Django 代码的其余部分配合使用。注意我们如何使用常用的 django 方法 order_by 和限制/切片查询集。