【问题标题】:Python/Django: Why does importing a module right before using it prevent a circular import?Python/Django:为什么在使用模块之前导入模块会阻止循环导入?
【发布时间】:2016-01-20 15:46:55
【问题描述】:

我在不同的情况下遇到过几次这个问题,但我的设置如下:

我有两个 Django 模型文件。包含用户可用于注册课程的用户模型和优惠券代码。这些都在 account/models.py 文件中。 Course 和相关的多对多字段位于不同的模型文件 course/models.py 中。我通常在我的代码中将它们分别称为 amod 和 cmod。

在 course/models.py 我有一个 import 声明:

from account import models as amod

class Course(ExtendedModel):
    stuff = stuff

我需要为 Course 和 User 之间的多对多模型/表导入 account/models.py 文件,此处未显示。到目前为止,一切顺利。

在 account/models.py 文件中,我有 CouponCode 模型。每个实例都会被创建,然后可以在创建后分配给特定的 Course 对象,以允许学生使用它来注册系统中的课程。

class CouponCode(ExtendedModel):
    assigned_course = UniqueIDForeignKey("course.Course", blank=True, null=True, related_name='assigned_coupon_code_set')
    ...
    ...

    @staticmethod
    def assign_batch(c, upper_limit):
        import course.models as cmod # Why is this allowed here?
        assert isinstance(c, cmod.Course)

        # Do other stuff here

该静态方法允许我传入一个课程对象和一些我想分配给它的优惠券代码,然后它将接下来的 N 个未分配代码分配给该课程。我的问题来自断言声明。

我需要从 course/models.py 导入 Course 对象,以确保传入的对象实际上是 Course 的一个实例,但如果我在文件顶部这样做,我会遇到问题,因为文件已被导入 course/models.py。 (amod 被导入到 cmod 中,然后在 amod 中我需要导入 cmod)。

如果我在需要它之前在方法中导入它而不是在文件顶部导入它,为什么它允许我这样做?

【问题讨论】:

    标签: python django import circular-dependency


    【解决方案1】:

    当一个模块被导入时(嗯,它是第一次在给定进程中导入),所有的顶级语句都会被执行(记住import一个可执行语句)。因此,您不能在顶层使用带有 import module2 语句的 module1 和在顶层使用带有 import module1 的 module2 - 它显然无法正常工作。

    现在如果在module2 中你将import module1 语句移动到一个函数中,这个语句在函数被实际调用之前不会被执行,所以它不会阻止module1 导入module2。

    请注意,这仍然被认为是不好的做法,大多数情况下,循环依赖意味着您应该重构代码以避免出现问题(将两个模块依赖的部分提取到第三个模块中,该模块依赖于两者但不依赖于其他模块取决于或简单地合并模块)-但由于其他限制,某些情况很复杂,因此将其作为最后的解决方案很好。

    另外,您不需要导入模型以在ForeignKeyMany2Many 字段中引用它 - 您可以传递"appname.ModelName" 字符串,参见https://docs.djangoproject.com/en/1.8/ref/models/fields/#foreignkey

    要引用另一个应用程序中定义的模型,您可以显式 指定具有完整应用程序标签的模型。例如,如果 上面的制造商模型在另一个应用程序中定义,称为 生产,你需要使用:

    class Car(models.Model):
        manufacturer = models.ForeignKey('production.Manufacturer')
    

    这种引用在解决循环导入时很有用 两个应用程序之间的依赖关系。

    【讨论】:

    • 根据您的经验,在创建外键而不是对类本身的引用时,有没有理由不使用字符串引用?对我来说,似乎总是这样做是避免问题的好方法。
    • @Spartacus 迟到的回答对不起 - 好吧,使用字符串引用会强制 orm 进行一些额外的查找并最终导入以解析引用,因此您在进程启动时会受到轻微的惩罚。这是否是一个问题取决于您的欣赏...但是您可以反过来问问题:为什么在您已经有了可用的类时使用字符串引用?-)
    猜你喜欢
    • 2020-11-05
    • 1970-01-01
    • 2012-03-07
    • 2015-10-06
    • 1970-01-01
    • 1970-01-01
    • 2011-01-25
    • 1970-01-01
    相关资源
    最近更新 更多