Hanpan
Hanpan

Reputation: 10251

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!

Upvotes: 181

Views: 87276

Answers (6)

AndyC
AndyC

Reputation: 129

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',...)

Upvotes: -2

ryansl39
ryansl39

Reputation: 9

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.

Upvotes: 0

Ranju R
Ranju R

Reputation: 2557

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')

Upvotes: 94

andilabs
andilabs

Reputation: 23281

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

Upvotes: 53

Bogatyr
Bogatyr

Reputation: 19323

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.

Upvotes: 60

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 798626

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

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

Upvotes: 332

Related Questions