Django - problema de importação de modelo circular

116

Eu realmente não estou entendendo isso, então se alguém pudesse explicar como isso funciona, eu apreciaria muito. Tenho dois aplicativos, Contas e Tema ... aqui está minha lista de configurações:

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

Em contas, estou tentando fazer isso:

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)

E no meu modelo de tema:

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á expulsando o seguinte erro:

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

É algum tipo de problema de importação circular? Tentei usar uma referência preguiçosa, mas também não parece funcionar!

Hanpan
fonte
1
Parece um problema com as importações circulares. Por que você precisa importar Accountdo módulo onde Themeestá definido?
Dominic Rodger
Desculpe, não colei meu modelo de Temas corretamente, atualizei minha postagem. Estou usando na aula de folha de estilo.
Hanpan

Respostas:

213

Remova a importação de Themee use o nome do modelo como uma string.

theme = models.ForeignKey('themes.Theme')
Ignacio Vazquez-Abrams
fonte
5
Na verdade, precisa ser 'themes.Theme', pois está em um aplicativo diferente.
Daniel Roseman
Ahh, funcionou, eu estava tentando apenas 'Theme' antes e não funcionou. Obrigado. Existe algum tipo de impacto no desempenho por fazer dessa maneira? Eu gostaria de manter minhas pesquisas não preguiçosas, se possível :)
Hanpan
@Daniel: Atualizado. @Hanpan: Um pequeno, sim. Mas apenas uma vez.
Ignacio Vazquez-Abrams
56

Upto Django 1.7:

Use a get_modelfunção django.db.modelsprojetada para importações de modelos preguiçosos:

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

No seu caso:

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

Agora você pode usar Theme

Para Django 1.7+:

from django.apps import apps
apps.get_model('app_label.model_name')
Ranju R
fonte
10
Use apps.get_model(app_label, model_name)ou apps.get_model('app_label.model_name') no Django 1.7+
phoibos
51

Algo que não vi mencionado em nenhum lugar com detalhes suficientes é como formular adequadamente a string dentro de ForeignKey ao fazer referência a um modelo em um aplicativo diferente. Esta string precisa ser app_label.model_name. E, o mais importante, app_labelnão é toda a linha no INSTALLED_APPS, mas apenas o último componente dela. Portanto, se o INSTALLED_APPS se parecer com isto:

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

em seguida, para incluir uma ForeignKey em um modelo em app2 em um modelo app1, você deve fazer:

app2_themodel = ForeignKey('app2.TheModel')

Passei um bom tempo tentando resolver um problema de importação circular (então não pude simplesmente from another.path.to.app2.models import TheModel) antes de me deparar com isso, google / SO não ajudou (todos os exemplos tinham caminhos de aplicativo de componente único), então espero que isso ajude outros novatos django.

Bogatyr
fonte
40

Já que o Django 1.7, a maneira correta é assim:

from django.apps import apps

YourModel = apps.get_model('your_app_name', 'YourModel')

Veja: https://docs.djangoproject.com/ja/1.9/ref/applications/#django.apps.apps.get_model

andilabs
fonte
5
Há também uma sintaxe de atalho de argumento único: apps.get_model('your_app_name.YourModel')conveniente para uso em mapetc.
Taylor Edmiston