【问题标题】:How to restrict Foreign Key choices to another Foreign Key in the same model如何将外键选择限制为同一模型中的另一个外键
【发布时间】:2013-06-25 22:28:57
【问题描述】:

我正在制作一个 django 应用程序,但遇到了一个问题。我正在尝试定义一个模型,其中一个 ForeignKey 将依赖于另一个 ForeignKey。

说明部分

我的申请是关于做出选择的。

假设您要创建一个decision,一个decision 有多个choices,一个choice 有一个status(由于其他限制)。

status 可以用于多个choices,但status 只能与一个decision 相关,choice 链接到同一个choice。

数据库架构

它不是固定的,如果需要可能会改变:

,____________,                  ,____________,
|            | 1, n        1, 1 |            |
|  Decision  |------------------|   Status   |
|____________|                  |____________|
      |                                |
      | 1, n                           | 1, n
      |                                |
      | 1, 1                           |
,_____|______,                         |
|            | 1, 1                    |
|   Choice   |-------------------------'
|____________|

代码

这是我当前的(简化的)(不工作)代码:

class Decision (models.Model):
    name = models.CharField (max_length = 63)

class Status (models.Model):
    value = models.CharField (max_length = 63)
    decision = models.ForeignKey (Decision)

class Choice (models.Model):
    name = models.CharField (max_length = 63)

    decision = models.ForeignKey (Decision)
    status = models.ForeignKey (Status, limit_choices_to = {'decision' : decision})

这里的重要部分是limit_choices_to = {'decision' : decision}。

额外信息

我发现另一个 SO question (In django, how to limit choices of a foreignfield based on another field in the same model?) 处理相同的问题,但问题已经过时了,最好的答案是依赖外部应用程序 (django-smart-selects)。

我宁愿不必使用外部的东西,而且我不明白为什么仅使用 Django 无法解决像 3 表关系这样简单的问题!

如果有人有任何解决方案或任何建议,请告诉我。

【问题讨论】:

  • 你看this answer了吗?
  • @karthikr 当我想将其直接应用于模型时,此答案适用于表单(同时简化了管理员管理)。但如果这不可能,这可能是我会选择的路径。
  • 嗯,你也可以在管理表单中做同样的事情。我知道它是多余的,但在你找到更好的方法之前,这可以工作
  • 这不能在模型级别完成;它只能在表单级别和数据库级别完成。
  • @Jerska 无法使用django-smart-selects,因为它在模型级别不起作用。它适用于表单级别。

标签: python django database-design django-models


【解决方案1】:

我不熟悉 Django,但如果您试图解决“选择链接到的同一个”部分问题,我可以在数据库级别这样做:

注意识别关系的用法,因此DecisionId 向下迁移到两个“分支”并在“底部”合并。因此,如果Choice 具有Status,则它们都必须链接到同一个Decision。

【讨论】:

  • 问题本身是 CW,所以所有答案也将是 CW。
  • 好吧,我看不到任何方法可以在 django 中将来自不同表的两个外键连接在一起,但这似乎是正确的数据库设计答案。
【解决方案2】:

您所要求的内容是不可能的,至少不在您设定的范围内(没有表单,没有外部库)。 status 你的 Choice 模型的字段是一个外键,两个表之间的关系......它本身不处理过滤,简单地说 - 它没有这个功能。这不是 django 的东西,这是 database 的东西。 Django ORM 并不像你想象的那样远离数据库,它很棒但并不神奇。

一些可用的解决方案是:

  • 通过过滤查询集在 FormField 级别执行此操作
  • 使用类似 django-smart-selects 之类的东西(以上内容)
  • 在您的模型上覆盖 save,在此处添加检查,如果失败则抛出错误
  • 将status 设为属性并在设置时对其进行验证检查

如果您使用 FormField 方法以及覆盖 save 您将受益于知道无法保存 Choice 它违反了此约束,无论从用户端(填写表单)或后端(在 Choice 实例上调用 .save() 的代码。

【讨论】:

  • 好吧,你的答案是我所期待的那种答案,即使我很想看到模型级别的解决方案。我的赏金去了。感谢您提供多种解决方案。
【解决方案3】:

我认为您在这里需要的是一个through 模型,如下所示:

class Choice (models.Model):
    name = models.CharField (max_length = 63)
    status = models.ForeignKey(Status)
    decision = models.ForeignKey(Decision)

class Status(Models.Model):
    name = models.CharField(max_length=20)

class Decision(models.Model):
    name = models.CharField(max_length = 63)
    choices = models.ManyToManyField(Status, through = "Choice")    

这样,每一个决定都有很多选择,每一个选择只有一个状态。您可以执行如下查询: my_decision.choices.all() 或 my_status.decision_set.all()

我建议您查看documentation 以获取有关如何通过模型使用的示例

【讨论】:

  • 我喜欢这个答案,因为它让我发现了through 参数,这实际上是一件非常有趣的事情。但你的答案实际上并不是我想要的,因为它仍然允许任何状态与选择相关联,即使现在状态和现在依赖于已经做出的选择的决定之间存在关系,这很有趣。我希望选择状态受到其决定的限制。但正如马特所指出的,这个问题似乎无法在 Django 的模型级别解决,正如我所想的那样。
【解决方案4】:

在以下语句中decision 既不是可调用对象也不是models.Q 对象:

status = models.ForeignKey (Status, limit_choices_to = {'decision' : decision})

这是一种表示数据的方法:

class Decision(models.Model):
    ...

# a status is relevant for only one decision
# there may be more than one Status per Decision.
class Status(Models.Model):
    decision = models.ForeignKey(Decision)

# each choice is linked to one decision and has a status.
class Choice (models.Model):
    status = models.ForeignKey(Status)
    # if status is mandatory, then you can get the decision
    # from status.decision. per se, this fk could be optional.
    decision = models.ForeignKey(Decision)

这是另一个:

class Decision(models.Model):
    ...

# a decision has multiple choices
# a choice pertains to only one decision
class Choice (models.Model):
    decision = models.ForeignKey(Decision)

# each status is relevant to one decision
# and may encompass multiple choices.
class Status(Models.Model):
    decision = models.ForeignKey(Decision)
    # problem with this representation is that this allows for
    # a choice to be linked to multiple statuses.
    # this happens when using M2M instead of ForeignKey.
    choices = models.ManyToManyField(Choice)

【讨论】:

  • 您的第一个答案实际上是我已经拥有的,而您的第二个答案只是删除约束,在存在OneToMany 的情况下建立ManyToMany 关系。但是你在发帖时就知道了,正如我和你的 cmets 所说的那样。不过,感谢您的回答。
  • 给出的第一个表示是正常的,这相当于@branko-dimitrijevic 的回答。
猜你喜欢
  • 2015-05-29
  • 1970-01-01
  • 1970-01-01
  • 2016-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多