Python/Django: Почему импорт модуля непосредственно перед его использованием предотвращает циклический импорт?

Я столкнулся с этой проблемой несколько раз в разных ситуациях, но моя настройка такова:

У меня есть два файла модели Django. Один, который содержит модели пользователей и купонные коды, которые пользователь может использовать для регистрации на курс. Они находятся в файле account/models.py. Курс и связанное с ним поле "многие ко многим" находятся в другом файле моделей, course/models.py. Я обычно отношусь к этим в моем коде как amod и cmod соответственно.

В курсе /models.py У меня есть оператор импорта:

from account import models as amod

class Course(ExtendedModel):
    stuff = stuff

Мне нужно импортировать файл account/models.py для модели/таблицы "многие ко многим" между курсом и пользователем, который здесь не показан. Пока что так хорошо.

В файле account/models.py у меня есть модель CouponCode. Каждый экземпляр создается и затем может быть назначен определенному объекту курса после создания, чтобы позволить учащемуся использовать его для регистрации курса в системе.

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 количество неназначенных кодов на этот курс. Мой вопрос возникает из утверждения assert.

Мне нужно импортировать объект курса с курса /models.py, чтобы гарантировать, что передаваемый объект является фактически экземпляром курса, но если я делаю это в верхней части файла, у меня возникают проблемы, потому что это файл уже импортируется в курс /models.py. (amod импортируется в cmod, а затем в amod мне нужно импортировать cmod).

Почему это позволяет мне это делать, если я импортирую его в метод прямо перед тем, как он мне понадобится, или в верхней части файла?

Ответ 1

Когда модуль импортируется (ну, в первый раз, когда он импортировался в данном процессе), все операторы верхнего уровня выполняются (помните, что import является исполняемым оператором). Таким образом, вы не можете иметь module1 с оператором import module2 на верхнем уровне и module2 с import module1 на верхнем уровне - он, очевидно, не может работать.

Теперь, если в модуле 2 вы перемещаете оператор import module1 внутри функции, этот оператор не будет выполняться до того, как функция будет фактически вызвана, поэтому это не помешает модулю1 импортировать модуль2.

Обратите внимание, что это по-прежнему считается плохой практикой, большую часть времени циклическая зависимость означает, что вы должны реорганизовать свой код, чтобы избежать проблемы (извлеките части, на которые оба модуля зависят, в третий модуль, который зависит от обоих, но ни один из других зависит от или просто объединить модули), но некоторые из них сложны из-за других ограничений, поэтому использование этого как решения последнего курорта является прекрасным.

Кроме того, вам не нужно импортировать модель для ссылки на нее в поле ForeignKey или Many2Many - вы можете передать строку "appname.ModelName", cf https://docs.djangoproject.com/en/1.8/ref/models/fields/#foreignkey

Чтобы ссылаться на модели, определенные в другом приложении, вы можете явно укажите модель с полной меткой приложения. Например, если Модель производителя выше определена в другом приложении, называемом продукции, вам необходимо использовать:

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

Такая ссылка может быть полезна при разрешении циклического импорта зависимостей между двумя приложениями.