Круговые зависимости модулей в Python / Django с разделенными моделями - PullRequest
6 голосов
/ 07 ноября 2011

Я понимаю, как разбивать модели, и понимаю, почему циклические зависимости модулей взрываются, но я столкнулся с проблемой, когда разбиение модели на отдельные файлы, по-видимому, вызывает циклические зависимости.Вот отрывок из кода, и я дополню его трассировкой от сбойного процесса:

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.

Самое странное, что весь сайт работает безупречно.Логины, покупки, все.Эта проблема возникает только тогда, когда я пытаюсь запустить сельдерей в фоновом режиме и загружается новая задача, или я пытаюсь запустить сценарий оболочки, используя среду Django.

Это мой единственный вариант удаления Permissionиз приложения ecom, или есть лучший, более умный способ справиться с этим?Кроме того, любые комментарии о том, как я изложил проект в целом, приветствуются.

1 Ответ

3 голосов
/ 07 ноября 2011

Ваша проблема в том, что Permission импортирует Product, но оба импортируются в ecom/models/__init__.py. Вы должны найти способ разместить эти две модели в одном файле или разделить их на два приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...