When developing Django models, it is very important to avoid circular dependencies especially in the models.py files. Circular dependencies are imports in different Python modules from each other. You should never cross-import from the different models.py files, because that causes serious stability issues. Instead, if you have interdependencies, you should use the actions described in this recipe.
Avoiding circular dependencies
Getting ready
Let's work with categories and ideas apps to illustrate how to deal with cross dependencies.
How to do it...
Follow these practices when working with models that use models from other apps:
- For foreign keys and many-to-many relationships with models from other apps, use the "<app_label>.<model>" declaration instead of importing the model. In Django this works with ForeignKey, OneToOneField, and ManyToManyField, for example:
# myproject/apps/ideas/models.py
from django.db import models
from django.conf import settings
from django.utils.translation import gettext_lazy as _
class Idea(models.Model):
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name=_("Author"),
on_delete=models.SET_NULL,
blank=True,
null=True,
)
category = models.ForeignKey(
"categories.Category",
verbose_name=_("Category"),
blank=True,
null=True,
on_delete=models.SET_NULL,
)
# other fields, attributes, properties and methods…
Here, settings.AUTH_USER_MODEL is a setting with a value such as "auth.User":
- If you need to access a model from another app in a method, import that model inside the method instead of at the module level, for example, as follows:
# myproject/apps/categories/models.py
from django.db import models
from django.utils.translation import gettext_lazy as _
class Category(models.Model):
# fields, attributes, properties, and methods…
def get_ideas_without_this_category(self):
from myproject.apps.ideas.models import Idea
return Idea.objects.exclude(category=self)
- If you use model inheritance, for example, for model mixins, keep the base classes in a separate app and place them before other apps that would use them in INSTALLED_APPS, as follows:
# myproject/settings/_base.py
INSTALLED_APPS = [
# contributed
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# third-party
# ...
# local
"myproject.apps.core",
"myproject.apps.categories",
"myproject.apps.ideas",
]
Here the ideas app will use the model mixins from the core app as follows:
# myproject/apps/ideas/models.py
from django.db import models
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from myproject.apps.core.models import (
CreationModificationDateBase,
MetaTagsBase,
UrlBase,
)
class Idea(CreationModificationDateBase, MetaTagsBase, UrlBase):
# fields, attributes, properties, and methods…
See also
- The Configuring settings for development, testing, staging, and production environments recipe in Chapter 1, Getting Started with Django 3.0
- The Respecting the import order in Python files recipe in Chapter 1, Getting Started with Django 3.0
- The Using model mixins recipe
- The Changing the foreign key to the many-to-many field recipe