Django - problem z importem modelu kołowego

116

Naprawdę tego nie rozumiem, więc gdyby ktoś mógł wyjaśnić, jak to działa, byłbym bardzo wdzięczny. Mam dwie aplikacje, konta i motyw ... oto moja lista ustawień:

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

Na kontach próbuję to zrobić:

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)

A w moim modelu motywu:

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 wyrzuca następujący błąd:

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

Czy to jakiś problem z importem w obiegu zamkniętym? Próbowałem użyć leniwego odniesienia, ale to też nie działa!

Hanpan
źródło
1
Wygląda to na problem z importem cyklicznym. Dlaczego musisz importować Accountz modułu, w którym Themejest zdefiniowane?
Dominic Rodger,
Przepraszamy, nie wkleiłem poprawnie modelu Motywy, zaktualizowałem swój post. Używam go w klasie Stylesheet.
Hanpan,

Odpowiedzi:

213

Usuń import Themei zamiast tego użyj nazwy modelu jako ciągu.

theme = models.ForeignKey('themes.Theme')
Ignacio Vazquez-Abrams
źródło
5
Właściwie to musi być 'themes.Theme', ponieważ jest w innej aplikacji.
Daniel Roseman,
Ach, to zadziałało, wcześniej próbowałem tylko „Theme” i nie zadziałało. Dzięki. Czy można to zrobić w ten sposób? Chciałbym, aby moje wyszukiwania nie były leniwe, jeśli to możliwe :)
Hanpan, 7-10
@Daniel: Zaktualizowano. @Hanpan: Mały, tak. Ale tylko raz.
Ignacio Vazquez-Abrams,
56

Upto Django 1.7:

Użyj get_modelfunkcji, z django.db.modelsktórej jest przeznaczona do leniwego importu modeli .:

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

W Twoim przypadku:

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

Teraz możesz użyć Theme

Dla Django 1.7+:

from django.apps import apps
apps.get_model('app_label.model_name')
Ranju R
źródło
10
Użyj apps.get_model(app_label, model_name)lub apps.get_model('app_label.model_name') w Django
1.7+
51

Coś, czego nie widziałem, o czym nigdzie nie wspomniałem wystarczająco szczegółowo, to sposób prawidłowego sformułowania ciągu wewnątrz ForeignKey podczas odwoływania się do modelu w innej aplikacji. Ten ciąg musi być app_label.model_name. I, co bardzo ważne, app_labelnie jest to cała linia w INSTALLED_APPS, ale tylko jej ostatni składnik . Więc jeśli Twoje INSTALLED_APPS wygląda tak:

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

następnie aby dołączyć klucz obcy do modelu w app2 w modelu app1, należy wykonać:

app2_themodel = ForeignKey('app2.TheModel')

Spędziłem sporo czasu próbując rozwiązać problem z importem cyklicznym (więc nie mogłem po prostu from another.path.to.app2.models import TheModel), zanim natknąłem się na to, Google / SO nie pomogło (wszystkie przykłady miały ścieżki aplikacji z jednym komponentem), więc mam nadzieję, że pomoże to innym nowicjusze django.

Bogatyr
źródło
40

Od Django 1.7 poprawnym sposobem jest takie postępowanie:

from django.apps import apps

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

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

andilabs
źródło
5
Istnieje również składnia skrótu z pojedynczym argumentem: apps.get_model('your_app_name.YourModel')wygodna do użycia w mapitp.
Taylor Edmiston,