Django应用依赖循环

27 投票
4 回答
9053 浏览
提问于 2025-04-15 11:45

我正在开发一个Django应用,这个应用的模型比较复杂(它模拟了一个大学,包括课程、模块、讲座、学生等等)。

为了让项目更有条理,我把它分成了几个小模块(这些小模块分别是课程、学校、人员、模块和时间段)。现在我遇到了一个问题,就是一个模块里的模型可能需要用到另一个模块里的模型,所以我必须去导入它。而第二个模块又需要用到第一个模块里的模型,这样就形成了一个循环,Python就会报错。

大家是怎么解决这个问题的呢?我知道小模块之间应该相对“独立”,但在这样的系统里,比如说用ContentTypes把学生和模块连接起来,这样做就不太合理。

有没有人有类似的项目,可以分享一下经验吗?

4 个回答

3

如果你遇到了循环模型依赖的问题,我猜可能有以下三种情况:

  • 你定义了一个反向关系,而这个关系已经存在了(比如说,一个课程有很多讲座,而讲座又只属于一个课程),在Django中这是多余的。
  • 你把模型的方法放在了错误的应用里。
  • 你在模型的方法中提供了本该在管理器里实现的功能。

也许你可以给我们看看这些模型的具体情况,这样我们可以一起找出问题的原因。循环模型依赖通常并不是说你需要把两个应用合并在一起,更可能(虽然不一定)是你的某个模型定义出了问题。

顺便说一下,我在做一个类似的Django应用,但我的应用结构可能和你的差别很大。如果你感兴趣,我很乐意给你简单介绍一下我的结构。

5

我很久以前写了以下内容。现在读起来,这对提问者的问题并不是很好的建议。他们可能应该把模型放在一个Django应用里。不过,以下内容是关于代码中依赖关系的一些基本背景,里面提到了一种“把它们放在一个组里”的解决方案:

不考虑Django的部分,解决循环依赖的一般方法是把其中一个相互引用的项目放到一个新的模块里。比如,下面这个循环:

moduleA: class1, class2
                       |        ^
                       v        |
moduleB: class3, class4

可以变成:

moduleB2:              class4
                                    |
                                    v
moduleA: class1, class2
                        |
                        v
moduleB1: class3

或者,你也可以把moduleA中的类拆分开:

moduleA1: class1
                       | 
                       v
moduleB: class3, class4
                                    |
                                    v
moduleA2:               class2

或者两者都做:

moduleA1: class1
                       | 
                       v
moduleB1: class3

moduleB2:              class4
                                      |
                                      v
moduleA2               class2

当然,如果类A和类B相互依赖,这样做就没什么帮助了:

moduleA: class1
                    |    ^
                    v    |
moduleB: class2

在这种情况下,也许它们应该放在同一个模块里(我觉得这可能是提问者应该采用的解决方案。把各种相关的模型类放在一个Django应用里):

moduleA: class1 <--->  class2

或者也许这些类可以以某种方式拆分开。比如,第一步可以把类1中类2需要的部分拆出来,放到一个新的类3里,这样原来的两个类都依赖于这个新类:

moduleA: class1
                    |    |
                    |    v
moduleB:   |    class2
                    |     |
                    v    v
moduleC: class3(new!)

这样就打破了循环依赖。我们可能还想进一步,把类2中类1依赖的部分也拆出来:

moduleA:       class1
                         |     |
                         |     |
moduleB:        |     |     class2
                         |     |      |    |
                         |     v     v   |
moduleC:       |    class3   |
                        |                   |
                        v                 v
moduleD:       class4(new!)

有时候,这种拆分是很困难的。掌握这个技巧确实需要一些努力。如果拆分得不好,结果的部分看起来很随意,特定于你的问题,你可能会觉得这样做反而让算法更难理解。但如果这个过程顺利,拆分出来的部分就像是有用的新基础构件,可以想象在其他地方使用(即使在你当前的项目中并不一定会这样),而使用它们会让你的核心算法更容易理解。

(记住,在一般情况下,上面的“依赖”箭头可以表示类之间的任何类型的依赖关系,比如继承、组合等等。一个类的方法可能只是调用另一个类的静态方法。而当我们谈论“类”时,这一切同样适用于其他逻辑块,比如相互依赖(即调用)的函数,或者相互引用的数据库表。)

61

如果你的依赖关系是外键指向其他应用里的模型,你不需要导入那个模型。你可以在外键定义中直接使用字符串:

class MyModel(models.Model):
    myfield = models.ForeignKey('myotherapp.MyOtherModel')

这样就不需要导入MyOtherModel,避免了循环引用的问题。Django会在内部处理这个字符串,一切都能正常工作。

撰写回答