【问题标题】:Django Multi-Column Foreign KeyDjango 多列外键
【发布时间】:2012-10-30 06:07:08
【问题描述】:

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

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

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

顺便说一句,我研究了django.contrib.contenttypes,但认为这不是这种情况的完美解决方案。

【问题讨论】:

    标签: django django-models


    【解决方案1】:

    目前还不支持。如果您愿意,有一张票和可能的处理方法。也许你甚至可以运行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:

    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

    【讨论】:

    • 非常感谢这个答案,它真的很有帮助! :D
    【解决方案2】:

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

    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))
    

    【讨论】:

    • 感谢您的回答,但恐怕这不是我正在谈论的 Django 模型的解决方案。数据库可能有复合外键,但似乎 Django 还没有提供这种支持。
    【解决方案3】:

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

    CREATE INDEX product_category_id_id ON product (category_id, id);
    

    为此,您必须在模型所在的子文件夹 sql 上创建一个名为 product.sql 的文件。夹具在初始同步数据库上加载。

    【讨论】:

      【解决方案4】:

      @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 方法限制/切片查询集。

      【讨论】:

        猜你喜欢
        • 2015-02-17
        • 2012-09-16
        • 2015-01-29
        • 2017-01-07
        • 1970-01-01
        • 1970-01-01
        • 2020-08-30
        • 1970-01-01
        • 2012-05-22
        相关资源
        最近更新 更多