2011-11-07 17 views
5

Entiendo cómo dividir modelos, y entiendo por qué las dependencias de módulos circulares explotan las cosas, pero me he encontrado con un problema donde dividir un modelo en archivos separados parece estar causando dependencias circulares. He aquí un extracto del código, y voy a seguir con el rastreo del proceso en su defecto:Dependencias de módulos circulares en Python/Django con modelos divididos

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) 

Por lo que puedo ver, no hay recursividad circular explícita él re, guarde para las referencias requeridas en __init__.py que parece estar donde las cosas están explotando en mi código. Aquí está el rastreo:

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 

Todo lo que estoy tratando de hacer aquí es que la importación Elearning modelo, que es una subclase de CompetencyProduct, ya su vez, Product. Sin embargo, debido a que Product proviene de una división del mayor ecom/models.py, el archivo ecom/__init__.py contiene la importación obligatoria de todos los modelos descompuestos, incluido Permission que tiene que importar Course que requiere CompetencyProduct.

Lo loco es que todo el sitio funciona correctamente. Logins, compras, todo. Este problema solo ocurre cuando intento ejecutar el apio en segundo plano y se carga una nueva tarea o trato de ejecutar un script de shell usando el entorno de Django.

¿Es mi única opción aquí eliminar Permission de la aplicación ecom, o hay una manera mejor y más inteligente de manejar esto? Además, cualquier comentario sobre cómo he presentado el proyecto en general es apreciado.

+0

¿Por qué molestarse en dar un ejemplo simplificado que * no * ilustra el problema? Tienes razón; no hay una dependencia circular allí, pero * está * en su código real. Publique el código actual, puede redactar todo lo que no corresponda, pero sería útil ver cómo se presentan sus archivos y modelos reales en su proyecto. –

+0

Tienes razón. Lo he cambiado ahora. –

Respuesta

3

Tu problema es que Permission importa , pero ambos se importan en ecom/models/__init__.py. Debería encontrar una forma de tener estos dos modelos en el mismo archivo o separarlos en dos aplicaciones.

+0

La cuestión es que, en realidad, * no necesito * esos modelos en '__init __. Py', excepto para syncdb para encontrar los modelos e instalarlos.Supongo que esperaba encontrar una forma de evitar colocar esas importaciones en ese archivo en primer lugar. –

+1

No es posible. Esa es la única forma de dividir tus modelos en archivos individuales con Django. Esas importaciones deben estar en '__init __. Py', así que, esencialmente, no se puede importar de forma cruzada dentro de la misma aplicación. Si dos modelos en la misma aplicación se necesitan, deben estar en el mismo archivo. –

Cuestiones relacionadas