Python/Django 模型文件中的循环依赖(不在 ForeignKey 等中)

1 投票
2 回答
533 浏览
提问于 2025-04-18 13:48

我有几个 models.py 文件,分别在不同的 Django 应用里。我在这些模型上有一些查询,这些查询在我的应用中会多次调用。我觉得为了避免重复代码,最好的办法就是把这些查询放在模型内部的方法里。

这些查询方法实际上会查询其他应用中的其他模型(也就是其他的 models.py 文件)。我知道这样会增加模块之间的耦合度,但因为这是一个大型且专业化的项目,所以我不能为很多东西写通用的可重用应用。

举个例子:

class Mentor(models.Model):
    # ...
    def get_future_shifts(self):
        return Shift.objects.filter(mentor = self, session__date__gt = timezone.now())

结果我遇到了循环依赖的问题(这个问题涉及到四个应用,所以我觉得不必要把所有代码都贴在这里)。

在 StackOverflow 上,关于 Django 模型的循环依赖的常见建议通常和 models.ForeignKey 有关,但这不是我的问题。我需要实际去 访问 这个“外部”的模型。

有人告诉我,循环依赖是设计不好的表现,而我设计不好的原因可能是我的模型里有太多动态的辅助方法?Django 其实没有其他地方可以放这些方法,而又不违反 DRY 原则。

2 个回答

0

你的 get_future_shifts() 方法暗示着 Shift 这个类和 Mentor 之间有外键关系。这样的话,你在包含 Mentor 的模块里就不需要导入 Shift,可以直接使用反向关系,也就是:

class Mentor(models.Model):
    # ...
    def get_future_shifts(self):
        return self.shift_set.filter(session__date__gt=timezone.now())

不过,这样做只是“技术上”解决了循环依赖的问题,因为这意味着 Mentor 直接知道另一个应用,而这个应用又依赖于定义 Mentor 的那个应用。如果你把这个其他应用从设置中移除,整个系统就会崩溃。

另一种解决方案是把 get_future_shifts 定义在和 Shift 同一个应用里,然后对 Mentor 进行“猴子补丁”,也就是:

from otherapp.models import Mentor

class Shift(models.Model):
    mentor = models.ForeignKey(Mentor)

# Extend Mentor with get_future_shifts helper    
def get_future_shifts(mentor):
    return mentor.shift_set.filter(session__date__gt=timezone.now())

Mentor.get_future_shifts = get_future_shifts

有些人可能会对从另一个模块或应用扩展类表示不满,但在这里仅仅在 Mentor 上定义一个外键就已经是在扩展 Mentor 了。所以至少我们把相关的东西放在一起,现在 Mentor 就不直接依赖于 Shift 了——只要 Mentor 的应用中的视图等不依赖于 get_future_shifts(),那么这就没问题。但这也意味着 Shift 应该和 Mentor 在同一个应用里。

2

有一种方法叫做 django.db.models.get_model(),它可以根据模型的名字来获取这个模型。这样做的好处是你并不需要直接导入这个模型。

撰写回答