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')
Такая ссылка может быть полезна при разрешении циклического импорта зависимостей между двумя приложениями.