Django - Problem beim Import von kreisförmigen Modellen

115

Ich verstehe das wirklich nicht. Wenn also jemand erklären könnte, wie das funktioniert, würde ich es sehr schätzen. Ich habe zwei Anwendungen, Konten und Thema ... hier ist meine Einstellungsliste:

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

In Konten versuche ich Folgendes:

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)

Und in meinem Themenmodell:

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 wirft den folgenden Fehler aus:

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

Ist dies eine Art zirkuläres Importproblem? Ich habe versucht, eine faule Referenz zu verwenden, aber das scheint auch nicht zu funktionieren!

Hanpan
quelle
1
Es sieht nach einem Problem mit zirkulären Importen aus. Warum müssen Sie Accountaus dem Modul importieren, in dem Themees definiert ist?
Dominic Rodger
Entschuldigung, ich habe mein Themes-Modell nicht richtig eingefügt. Ich habe meinen Beitrag aktualisiert. Ich benutze es in der Stylesheet-Klasse.
Hanpan

Antworten:

211

Entfernen Sie den Import von Themeund verwenden Sie stattdessen den Modellnamen als Zeichenfolge.

theme = models.ForeignKey('themes.Theme')
Ignacio Vazquez-Abrams
quelle
5
Eigentlich muss das so sein 'themes.Theme', wie es in einer anderen App ist.
Daniel Roseman
Ahh, das hat funktioniert, ich habe vorher nur 'Theme' ausprobiert und es hat nicht funktioniert. Vielen Dank. Gibt es einen Performance-Hit dafür? Ich möchte meine Lookups wenn möglich nicht faul halten :)
Hanpan
@ Daniel: Aktualisiert. @ Hanpan: Ein kleiner, ja. Aber nur einmal.
Ignacio Vazquez-Abrams
56

Bis zu Django 1.7:

Verwenden Sie eine get_modelFunktion, django.db.modelsdie für verzögerte Modellimporte ausgelegt ist:

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

In deinem Fall:

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

Jetzt können Sie verwenden Theme

Für Django 1.7+:

from django.apps import apps
apps.get_model('app_label.model_name')
Ranju R.
quelle
10
Verwendung apps.get_model(app_label, model_name)oder apps.get_model('app_label.model_name') in Django 1.7+
Phoibos
51

Etwas, das ich nirgends ausführlich genug erwähnt habe, ist die korrekte Formulierung der Zeichenfolge in ForeignKey, wenn auf ein Modell in einer anderen App verwiesen wird. Diese Zeichenfolge muss sein app_label.model_name. Und vor allem ist das app_labelnicht die gesamte Zeile in INSTALLED_APPS, sondern nur die letzte Komponente davon. Wenn Ihr INSTALLED_APPS also so aussieht:

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

Um einen ForeignKey in ein Modell in App2 in einem App1-Modell aufzunehmen, müssen Sie Folgendes tun:

app2_themodel = ForeignKey('app2.TheModel')

Ich habe ziemlich lange versucht, ein Problem mit dem zirkulären Import zu lösen (also konnte ich es nicht einfach from another.path.to.app2.models import TheModel), bevor ich darauf gestoßen bin. Google / SO war keine Hilfe (alle Beispiele hatten Einzelkomponenten-App-Pfade). Hoffentlich hilft dies anderen Django-Neulinge.

Bogatyr
quelle
40

Da Django 1.7 der richtige Weg ist, so zu gehen:

from django.apps import apps

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

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

Andilabs
quelle
5
Es gibt auch eine Single-Arg-Shortcut-Syntax: apps.get_model('your_app_name.YourModel')praktisch für die Verwendung in einem mapusw.
Taylor Edmiston