It's quite easy to run into import problems with Django and apps which interact with each other. My question is simple:
What is the accepted process for minimizing circular imports or has anyone come up with an accepted coding standard to reduce these which they are willing to share.?
I'm looking for good principles that can be standardized on.
Models
class Program(models.Model):
group = models.ForeignKey(Group, related_name="%(app_label)s_%(class)s_related")
vs
class Program(models.Model):
group = models.ForeignKey('auth.Group', related_name="%(app_label)s_%(class)s_related")
Views:
class ProgramDetailView(DetailView):
"""Detail view of the EEP Program"""
def get_queryset(self):
"""Narrow this based on your company"""
from apps.company.models import Company
company = Company.objects.get(name="foo")
return Program.objects.filter(company = company)
vs (which tends to cause the issues..
from apps.company.models import Company
class ProgramDetailView(DetailView):
"""Detail view of the EEP Program"""
def get_queryset(self):
"""Narrow this based on your company"""
company = Company.objects.get(name="foo")
return Program.objects.filter(company = company)
The problem with this is that you tend to do a lot of imports all over the place..
To do this, create a directory called /«project»/«app_name»/models , inside it put __init__.py (to declare it as a module) and then create your files inside there. You then need to import your file contents into the module in __init__.py . You should read about Python modules to understand this.
The AppConfig class used to configure the application has a path class attribute, which is the absolute directory path Django will use as the single base path for the application.
Over the years I standardized on some patterns, based on my observations on how I develop web apps.
I don't know what are your standards about modularity and code re-use but the following simple rules/patterns have helped me greatly with some fairly large projects.
I have noticed that many of my models share some common attributes. For example I prefer to use UUID's instead of simple auto increment integers, as primary keys.
So I have this abstract model.
class UUIDModel(models.Model):
id = UUIDField(primary_key=True, auto=True) # There are many implementation of this on the web. Choose your favorite.
class Meta:
abstract = True
Many of my models need the the concept of activation
. So I have another abstract model,
similar to this:
class ActivatedModel(Model):
is_active = models.BooleanField(default=False)
def activate(self, save=True):
if self.is_active:
raise Exception('Already activated')
self.is_active = True
if save:
self.save()
class Meta:
abstract = True
There are many other abstract models I use for tracking creation time and modification, or
if something is finalized
and can't be further modified, etc.
All these abstract models live in the core
app. This is how I call it.
Apart from the core
app, I have a tasks
app. The tasks
app offers abstract
models that augments any interfacing I have to do with celery which I use a lot.
The tasks
app can import models from the core
app, but not the other way around.
I have also an mms
app, which handles Multimedia creation and transformations(thumbnails, etc). The mms
can import models from the previous apps. So the import relationship we have right now is this: core -> tasks -> mms.
Every other app I create is specific to the current project I am working on and builds upon the previous apps. So basically I try to have "one way imports" if you can call it that.
And I end up with models that look similar to this:
# models.py of an app called "articles"
from core.models import UUIDModel, ActivatedModel
from tasks.models import ManagedTasksModel
class Article(UUIDModel, ActivatedModel, ManagedTasksModel):
title = models.CharField()
# blah...
If an app grows too big I "micromanage" the app by breaking the models.py
module into
smaller modules following the above mentioned rules. I have found out that this covers
most of my needs.
I can't comment on class based views, because honestly I don't like them and it almost always make me write more code instead of less. To each his own. I prefer to use helper utility functions and make good use of things like context processors, inside my view functions.
I hope my answer was within the context of your question.
EDIT: I just noticed your usage of related_name
which I think misses the point of that option. See the following example:
class Message(models.Model):
sender = models.ForeignKey(User, related_name='messages_sent')
receiver = models.ForeignKey(User, related_name='messages_received')
body = models.Textfield()
With the above model we can do this, which is very readable:
u1 = User.objects.get(...)
received = u1.messages_received.all()
... and it depicts the functional purpose of this relationship. So related_name
is not
only used for having related names which are unique.
I don't think there is a high probability that the view code from app A would import the view code from app B in such a way that a circular import is caused.
If you happen to be using view code from another app B in app A's views, then that code should belong to a views.py
file, but to an utils.py
(or similar) file.
It is even more unlikely that the model code from app A would import the view code from app B, so it's totally OK to use a top-level import for app A's model code in app B's view code.
As you mentioned however, having ForeignKey
relationships between models from different apps can lead to circular imports.
Using the string notation for your ForeignKey
addresses that challenge, and as the Django docs put it:
If you need to create a relationship on a model that has not yet been defined, you can use the name of the model, rather than the model object itself.
This also works for ManyToMany
and OneToOne
relationships.
I don't think there really exists one, but I figured that maybe looking at django.contrib
apps would be relevant. They use model imports.
You might want to check out:
Popular apps also use the same functionnality:
You might also want to have a look at the source of social_auth
for a variation on this:
I think that a general rule of thumb would be to use model imports when you can, but be ready to use string notation if needed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With