Django - Problema de importación de modelo circular

116

Realmente no entiendo esto, así que si alguien pudiera explicar cómo funciona esto, lo agradecería mucho. Tengo dos aplicaciones, Cuentas y Tema ... aquí está mi lista de configuraciones:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'accounts',
    'themes',
)

En cuentas, estoy tratando de hacer esto:

from themes.models import Theme

class Account(models.Model):
    ACTIVE_STATUS = 1
    DEACTIVE_STATUS = 2
    ARCHIVE_STATUS = 3
    STATUS_CHOICES = (
        (ACTIVE_STATUS, ('Active')),
        (DEACTIVE_STATUS, ('Deactive')),
        (ARCHIVE_STATUS, ('Archived')),
    )

    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=250)
    slug = models.SlugField(unique=True, verbose_name='URL Slug')
    status = models.IntegerField(choices=STATUS_CHOICES, default=ACTIVE_STATUS, max_length=1)
    owner = models.ForeignKey(User)
    enable_comments = models.BooleanField(default=True)
    theme = models.ForeignKey(Theme)
    date_created = models.DateTimeField(default=datetime.now)

Y en mi modelo temático:

class Theme(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=250)
    slug = models.SlugField(unique=True, verbose_name='URL Slug')
    date_created = models.DateTimeField(default=datetime.now)

class Stylesheet(models.Model):
    id = models.AutoField(primary_key=True)
    account = models.ForeignKey(Account)
    date_created = models.DateTimeField(default=datetime.now)
    content = models.TextField()

Django está lanzando el siguiente error:

from themes.models import Theme
ImportError: cannot import name Theme

¿Es este algún tipo de problema de importación circular? Intenté usar una referencia perezosa, ¡pero eso tampoco parece funcionar!

Hanpan
fuente
1
Parece un problema con las importaciones circulares. ¿Por qué necesita importar Accountdesde el módulo donde Themeestá definido?
Dominic Rodger
Lo siento, no pegué mi modelo de Temas correctamente, he actualizado mi publicación. Lo estoy usando en la clase de hoja de estilo.
Hanpan

Respuestas:

213

Elimine la importación de Themey use el nombre del modelo como una cadena en su lugar.

theme = models.ForeignKey('themes.Theme')
Ignacio Vázquez-Abrams
fuente
5
En realidad, tiene que ser así 'themes.Theme', ya que está en una aplicación diferente.
Daniel Roseman
Ahh, eso funcionó, estaba probando solo 'Tema' antes y no funcionó. Gracias. ¿Hay algún tipo de impacto en el rendimiento por hacerlo de esta manera? Me gustaría mantener mis búsquedas no perezosas si es posible :)
Hanpan
@Daniel: actualizado. @Hanpan: Uno pequeño, sí. Pero solo una vez.
Ignacio Vazquez-Abrams
56

Hasta Django 1.7:

Utilice la get_modelfunción django.db.modelsque está diseñada para las importaciones de modelos perezosos

from django.db.models import get_model
MyModel = get_model('app_name', 'ModelName')

En tu caso:

from django.db.models import get_model
Theme = get_model('themes', 'Theme')

Ahora puedes usar Theme

Para Django 1.7+:

from django.apps import apps
apps.get_model('app_label.model_name')
Ranju R
fuente
10
Usar apps.get_model(app_label, model_name)o apps.get_model('app_label.model_name') en Django 1.7+
phoibos
51

Algo que no he visto mencionado en ninguna parte con suficiente detalle es cómo formular correctamente la cadena dentro de ForeignKey al hacer referencia a un modelo en una aplicación diferente. Esta cadena debe ser app_label.model_name. Y, lo que es más importante, app_labelno es la línea completa en INSTALLED_APPS, sino solo el último componente de la misma. Entonces, si su INSTALLED_APPS se ve así:

INSTALLED_APPS = (
...
    'path.to.app1',
    'another.path.to.app2'
)

luego, para incluir una ForeignKey a un modelo en app2 en un modelo app1, debe hacer:

app2_themodel = ForeignKey('app2.TheModel')

Pasé bastante tiempo tratando de resolver un problema de importación circular (por lo que no pude simplemente from another.path.to.app2.models import TheModel) antes de tropezar con esto, google / SO no fue de ayuda (todos los ejemplos tenían rutas de aplicación de un solo componente), así que espero que esto ayude a otros novatos de django.

Bogatyr
fuente