Kye
Kye

Reputation: 4495

Python/Django circular dependency with models.py (NOT in ForeignKey.etc)

I have x models.py files, in x different Django apps. I have certain queries related to said models thatI call throughout my app. I figured the best way to DRY-ify this is to have the query be called by a method inside the model.

These query methods actually query /other/ models inside /other/ apps (and therefore other models.py files). I understand that this increases coupling, but it's a large and highly specialised project, so I can't really write generic reusable apps for a lot of the stuff.

For example:

class Mentor(models.Model):
    # ...
    def get_future_shifts(self):
        return Shift.objects.filter(mentor = self, session__date__gt = timezone.now())

I've ended up with a circular dependency (it spans along 4 apps so I figured it was too long to post ALL that code here unless absolutely necessary).

The usual circular dependency advice for Django models on SO is related to models.ForeignKey and that is not my issue. I need to actually access the 'foreign' model.

I'm told that a circular dependency is a sign of bad design, and my bad design is that I have too many dynamic helper methods in my models? Django doesn't really provide anywhere else to put these without adhering to DRY.

Upvotes: 1

Views: 540

Answers (2)

bruno desthuilliers
bruno desthuilliers

Reputation: 77892

Your get_future_shifts() method implies that Shift has a FK on Mentor. In this case, you don't need to import Shift in the module containing Mentor, you can just use the reverse relationship ie:

class Mentor(models.Model):
    # ...
    def get_future_shifts(self):
        return self.shift_set.filter(session__date__gt=timezone.now())

but this will only "technically" solve the circular dependency, since it means that Mentor has some direct knowledge of another app that itself depends on the app where Mentor is defined and the whole thing will break if you remove this other app from your settings.

Another solution is to define get_future_shifts in the same app as Shift' and monkeypatchMentor`, ie:

from otherapp.models import Mentor

class Shift(models.Model):
    mentor = models.ForeignKey(Mentor)

# Extend Mentor with get_future_shifts helper    
def get_future_shifts(mentor):
    return mentor.shift_set.filter(session__date__gt=timezone.now())

Mentor.get_future_shifts = get_future_shifts

Some will frown upon extending a class from another module / app, but just defining a FK on Mentor here is already extending Mentor, so at least we keep related stuff together and now Mentor has no direct dependency on Shift - as long as nothing in Mentor's app's views etc depends on get_future_shifts() at least, but then it means that Shifts should really belong to the same app as Mentor.

Upvotes: 0

Kye
Kye

Reputation: 4495

There exists a method, django.db.models.get_model(), that gets a model given its name. This'll fix it as you aren't actually importing the model.

Upvotes: 2

Related Questions