【问题标题】:django models - Avoid possible circular reference in database designdjango 模型 - 避免数据库设计中可能的循环引用
【发布时间】:2026-02-22 18:10:02
【问题描述】:

我有一个数据库设计和关系问题,我担心可能的循环引用。

举个例子,Jack 的库存药品 A、B 和 C。药品 A 和 B 有一个 active_ingredient AI1,而药品 C 有一个 active_ingredient AI2。

杰克去看医生,医生给他开了 AI1。对于处方对象,吃药A还是药B无所谓。

这是一个示例代码:

class ActiveIngredient(models.Model):
    ...

class Medicine(models.Model):
    quantity = models.IntegerField()
    active_ingredient = models.ForeignKey("ActiveIngredient", on_delete=models.CASCADE)

class Person(models.Model):
    ...

class PersonStock(models.Model):
    customer = models.ForeignKey("Person", on_delete=models.CASCADE)
    medicine = models.ForeignKey("Medicine", on_delete=models.CASCADE)
    expiration_date = models.DateField()

class Prescription(models.Model):
    ...
    quantity = models.IntegerField()

模拟这种关系的最佳解决方案是什么?

把处方改成这样:

class Prescription(models.Model):
    ...
    customer = models.ForeignKey("Person", on_delete=models.CASCADE)
    active_ingredient = models.ForeignKey("ActiveIngredient", on_delete=models.CASCADE)
    quantity = models.IntegerField()

鉴于 PersonStock 类已经将 Person 和 Medicine 连接起来,这对我来说似乎是错误的。

【问题讨论】:

  • @KevinChristopherHenry 问题是医生开的是活性成分,而不是药物。 PersonStock 是指一个人拥有的药物,处方是规定在一定时间内服用的活性成分。我的问题是对 Prescription 模型进行建模,因为我觉得与 ActiveIngredient 有关系并不合适,因为已经有一个 PersonStock 连接 Person 和 Medicine(并且,通过关联,ActiveIngredient)。我看错了吗?
  • 我同意@KevinChristopherHenry 在下面的回答,但要补充一点,您在这里提到的两种关系的用途非常不同。能够单独列出患者订购的药物与为其开具的活性成分是有价值的。除了错误之外,这还允许用户检查未填写的处方。

标签: django database django-models


【解决方案1】:

您担心重复信息是对的;数据库设计(特别是数据库规范化)的一个主要问题是避免这种情况,以消除数据不一致的可能性。

但是,在这种情况下,我认为将处方与其填充分开更有意义。这是两件不同的事情,在现实世界中,很可能会犯错误并提供错误的药物。虽然人们应该努力防止此类错误,但这与无法在数据模型中表示错误是截然不同的。

所以我的建议是在应用层使用validate the data,而不是在数据模型本身中构建约束。比如:

class ActiveIngredient(models.Model):
    ...

class Medicine(models.Model):
    quantity = models.IntegerField()
    active_ingredient = models.ForeignKey("ActiveIngredient", on_delete=models.CASCADE)

class Person(models.Model):
    ...

class Prescription(models.Model):
    ...
    customer = models.ForeignKey("Person", on_delete=models.CASCADE)
    active_ingredient = models.ForeignKey("ActiveIngredient", on_delete=models.CASCADE)
    quantity = models.IntegerField()

class PersonStock(models.Model):
    prescription = models.ForeignKey("Prescription", on_delete=models.CASCADE)
    medicine = models.ForeignKey("Medicine", on_delete=models.CASCADE)
    expiration_date = models.DateField()

    # Make sure the supplied medicine is correct.
    def clean(self):
        if self.medicine.active_ingredient != self.prescription.active_ingredient:
            raise ValidationError("Wrong medicine!")

或者,您可以仅在创建 PersonStock 时进行检查。

【讨论】: