#python #django
#python #django
Вопрос:
Я понимаю, как разбивать модели, и я понимаю, почему циклические зависимости модулей все портят, но я столкнулся с проблемой, когда разбиение модели на отдельные файлы, по-видимому, вызывает циклические зависимости. Вот выдержка из кода, и я добавлю к ней обратную трассировку из сбоящего процесса:
elearning/tasks.py
from celery.task import task
@task
def decompress(pk):
from elearning.models import Elearning
Elearning.objects.get(pk=pk).decompress()
elearning/models.py
from competency.models import CompetencyProduct
from core.helpers import ugc_elearning
from elearning.fields import ArchiveFileField
class Elearning(CompetencyProduct):
archive = ArchiveFileField(upload_to=ugc_elearning)
def decompress(self):
import zipfile
src = self.archive.path
dst = src.replace(".zip","")
print "Decompressing %s to %s" % (src, dst)
zipfile.ZipFile(src).extractall(dst)
ecom/models/products.py
from django.db import models
from django.utils.translation import ugettext_lazy as _
from core.models import Slugable, Unique
from django_factory.models import Factory
from core.helpers import ugc_photos
class Product(Slugable, Unique, Factory):
photo = models.ImageField(upload_to=ugc_photos, width_field="photo_width", height_field="photo_height", blank=True)
photo_width = models.PositiveIntegerField(blank=True, null=True, default=0)
photo_height = models.PositiveIntegerField(blank=True, null=True, default=0)
description = models.TextField()
price = models.DecimalField(max_digits=16, decimal_places=2)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
ecom/models/__init__.py
from django.contrib.auth.models import User
from django.db import models
from ecom.models.products import Product, Credit, Subscription
from ecom.models.permissions import Permission
from ecom.models.transactions import Transaction, DebitTransaction, CreditTransaction, AwardTransaction, FinancialTransaction, PermissionTransaction, BundleTransaction
competency/models.py
from django.db import models
from django.utils.translation import ugettext_lazy as _
from core.models import Slugable, Unique
from ecom.models import Product
from rating.models import Rated
from trainer.models import Trainer
class Competency(Slugable, Unique):
class Meta:
verbose_name = _("Competency")
verbose_name_plural = _("Competencies")
description = models.TextField()
class CompetencyProduct(Product, Rated):
class Meta:
verbose_name = _("Product")
verbose_name_plural = _("Products")
release = models.DateField(auto_now_add=True)
trainers = models.ManyToManyField(Trainer)
competencies = models.ManyToManyField(Competency, related_name="provided_by")
requirements = models.ManyToManyField(Competency, related_name="required_for", blank=True, null=True)
forsale = models.BooleanField("For Sale", default=True)
ecom/models/permissions.py
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import ugettext_lazy as _
from treebeard.mp_tree import MP_Node
from collective.models import Collective
from course.models import Course
from ecom.models.products import Product
class Permission(MP_Node):
class Meta:
app_label = "ecom"
product = models.ForeignKey(Product, related_name="permissions")
user = models.ForeignKey(User, related_name="permissions")
collective = models.ForeignKey(Collective, null=True)
course = models.ForeignKey(Course, null=True)
redistribute = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
accessed = models.DateTimeField(auto_now=True)
course/models.py
from django.db import models
from django.utils.translation import ugettext_lazy as _
from competency.models import CompetencyProduct
from ecom.models import Product
from rating.models import Rated
class Chapter(models.Model):
seq = models.PositiveIntegerField(name="Sequence", help_text="Chapter number")
name = models.CharField(max_length=128)
note = models.CharField(max_length=128)
class Course(Product, Rated):
level = models.PositiveIntegerField(choices=CompetencyProduct.LEVELS)
chapters = models.ManyToManyField(Chapter)
class Bundle(models.Model):
class Meta:
unique_together = (("product", "chapter"),)
product = models.ForeignKey(Product, related_name="bundles")
chapter = models.ForeignKey(Chapter, related_name="bundles")
amount = models.PositiveIntegerField()
seq = models.PositiveIntegerField(name="Sequence", default=1)
Из того, что я вижу, здесь нет явной циклической рекурсии, за исключением требуемых ссылок в __init__.py
, которые, похоже, являются тем местом, где в моем коде происходят сбои. Вот обратная трассировка:
File "/path/to/project/virtualenv/lib/python2.6/site-packages/celery/execute/trace.py", line 47, in trace
return cls(states.SUCCESS, retval=fun(*args, **kwargs))
File "/path/to/project/virtualenv/lib/python2.6/site-packages/celery/app/task/__init__.py", line 247, in __call__
return self.run(*args, **kwargs)
File "/path/to/project/virtualenv/lib/python2.6/site-packages/celery/app/__init__.py", line 175, in run
return fun(*args, **kwargs)
File "/path/to/project/django/myproj/elearning/tasks.py", line 5, in decompress
from elearning.models import Elearning
File "/path/to/project/django/myproj/elearning/models.py", line 2, in <module>
from competency.models import CompetencyProduct
File "/path/to/project/django/myproj/competency/models.py", line 5, in <module>
from ecom.models import Product
File "/path/to/project/django/myproj/ecom/models/__init__.py", line 5, in <module>
from ecom.models.permissions import Permission
File "/path/to/project/django/myproj/ecom/models/permissions.py", line 8, in <module>
from course.models import Course
File "/path/to/project/django/myproj/course/models.py", line 4, in <module>
from competency.models import CompetencyProduct
ImportError: cannot import name CompetencyProduct
Все, что я пытаюсь здесь сделать, это импортировать эту Elearning
модель, которая является подклассом CompetencyProduct
, и, в свою очередь, Product
. Однако, поскольку Product
происходит из-за разделения более крупных моделей, ecom/models.py
файл ecom/__init__.py
содержит обязательный импорт всех разделенных моделей, включая Permission
которые должны импортировать, Course
что требует CompetencyProduct
.
Самое странное, что весь сайт работает идеально. Входы в систему, покупки, все. Эта проблема возникает только тогда, когда я пытаюсь запустить celery в фоновом режиме и загружается новая задача, или я пытаюсь запустить сценарий оболочки, используя среду Django.
Мой единственный вариант здесь — удалить Permission
из ecom
приложения, или есть лучший, более разумный способ справиться с этим? Кроме того, приветствуются любые комментарии о том, как я изложил проект в целом.
Комментарии:
1. Зачем утруждать себя приведением упрощенного примера, который не иллюстрирует проблему? Вы правы; там нет циклической зависимости, но она есть в вашем реальном коде. Публикуя свой фактический код, вы можете отредактировать все, что не применимо, но было бы полезно увидеть, как ваши фактические файлы и модели размещены в вашем проекте.
2. Вы правы. Сейчас я это изменил.
Ответ №1:
Ваша проблема в том, что Permission
импортируется Product
, но оба импортируются в ecom/models/__init__.py
. Вы должны найти способ либо поместить эти две модели в один файл, либо разделить их на два приложения.
Комментарии:
1. Дело в том, что мне на самом деле не нужны эти модели в
__init__.py
, кроме как для syncdb, чтобы найти модели и установить их. Думаю, я надеялся найти способ избежать помещения этих импортированных данных в этот файл в первую очередь.2. Невозможно. Это единственный способ разделить ваши модели на отдельные файлы с помощью Django. Эти импорта должны быть в
__init__.py
, поэтому, по сути, вы не можете выполнять перекрестный импорт в одном приложении. Если две модели в одном приложении нуждаются друг в друге, они должны находиться в одном файле.3. Безумно расстраивает, если на самом деле нет обходного пути. Почти все мои классы перекрестно зависят друг от друга..