ChatGPT解决这个技术问题 Extra ChatGPT

Django - Circular model import issue

I'm really not getting this, so if someone could explain how this works I'd very much appreciate it. I have two applications, Accounts and Theme... here is my settings list:

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

In accounts, I am trying to do this:

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)

And in my theme model:

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 is kicking out the following error:

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

Is this some kind of circular import issue? I've tried using a lazy reference, but that doesn't seem to work either!

It does look like an issue with circular imports. Why do you need to import Account from the module where Theme is defined?
Sorry, I did not paste my Themes model correctly, I have updated my post. I am using it in the Stylesheet class.

I
Ignacio Vazquez-Abrams

Remove the import of Theme and use the model name as a string instead.

theme = models.ForeignKey('themes.Theme')

Actually that needs to be 'themes.Theme', as it's in a different app.
Ahh, that worked, I was trying just 'Theme' before and it didn't work. Thanks. Is there any kind of performance hit for doing it this way? I'd like to keep my lookups non lazy if possible :)
@Daniel: Updated. @Hanpan: A small one, yes. But only once.
R
Ranju R

Upto Django 1.7:

Use get_model function from django.db.models which is designed for lazy model imports.:

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

In your case:

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

Now you can use Theme

For Django 1.7+:

from django.apps import apps
apps.get_model('app_label.model_name')

Use apps.get_model(app_label, model_name) or apps.get_model('app_label.model_name') in Django 1.7+
B
Bogatyr

Something I haven't seen mentioned anywhere in sufficient detail is how to properly formulate the string inside ForeignKey when referencing a model in a different app. This string needs to be app_label.model_name. And, very importantly, the app_label is not the entire line in INSTALLED_APPS, but only the last component of it. So if your INSTALLED_APPS looks like this:

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

then to include a ForeignKey to a model in app2 in an app1 model, you must do:

app2_themodel = ForeignKey('app2.TheModel')

I spent quite a long time trying to solve a circular import issue (so I couldn't just from another.path.to.app2.models import TheModel) before I stumbled onto this, google/SO was no help (all the examples had single component app paths), so hopefully this will help other django newbies.


a
andilabs

Since Django 1.7 correct way is to go like this:

from django.apps import apps

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

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


There's also a single-arg shortcut syntax: apps.get_model('your_app_name.YourModel') convenient for use in a map etc.
r
ryansl39

I had the same problem but on using MyModel = get_model('app_name', 'ModelName') I got a new error 'raise AppRegistryNotReady("Models aren't loaded yet.")'

I tried all the other methods on this page but none worked for me. The way I fixed it was to use: MyModel = get_model('app_name', 'ModelName') but actually in the class rather than outside of it.


Could you please elaborate?
In models.py from django.apps import apps I then defined a save method which inside has: Cart = apps.get_model(app_label='Carts', model_name='Cart') This allows me to import another django model from elsewhere.
A
AndyC

This is how I fix the circular issue by specifying 'app_name.model_name' and TYPE_CHECKING

profile app's models.py
   from typing import TYPE_CHECKING
   if TYPE_CHECKING:
     from team.models import Team

   team = models.ForeignKey('team.Team',...)

team app's models.py  
  Class Team
    name = models.CharField(...) 
    head = models.ForeignKey('profile.Profile',...)