Circular imports between managers and models

1,849 views
Skip to first unread message

Polat Tuzla

unread,
Jan 6, 2009, 7:42:22 AM1/6/09
to Django users
Hi,
Suppose I have two classes in "models.py", namely A and B. And there
is the manager for B as BManager in "managers.py". BManager makes use
of clas A.

This situation leads to circular imports between "managers.py" and
"models.py" for which I can't find a solution.

Assuming that I need to separate models and manager into different
files, so merging them is not an option, are there any best practices
or do you have any other suggestions?

Thanks,
Polat Tuzla

Lee Braiden

unread,
Jan 6, 2009, 8:08:33 AM1/6/09
to django...@googlegroups.com

No expert on Django, and it's hard to tell what your GOAL is from this
description, but you might want I'd write something like this:

class Employee(models.Model):
name = models.CharField(...)

class Manager(Employee):
manager_stuff...

class Worker(Employee):
worker_stuff...
manager = models.ForeignKey(Manager,related_name="subordinates",
blank=True, null=True)

class WorkerTypeA(Worker):
workera_stuff..

class WorkerTypeB(Worker):
workerb_stuff...

class ManagerB(Manager):
a_person = ForeignKey(
more_manager_b_stuff...

Again, depending on what your goal is, you might not even need that
a_person field now, since Workers can have managers, and managers can
have subordinates.

--
Lee


bruno desthuilliers

unread,
Jan 6, 2009, 8:21:32 AM1/6/09
to Django users
The import statement works fine in a method too.

class BManager(...):
def some_method(self):
from models import A
# code here


But I have hard time understanding why you couldn't put models and
managers in the models file...

bruno desthuilliers

unread,
Jan 6, 2009, 8:37:47 AM1/6/09
to Django users
On 6 jan, 14:08, Lee Braiden <fallibledra...@gmail.com> wrote:
> On Tue, 2009-01-06 at 04:42 -0800, Polat Tuzla wrote:
> > Hi,
> > Suppose I have two classes in "models.py", namely A and B. And there
> > is the manager for B as BManager in "managers.py". BManager makes use
> > of clas A.
>
> > This situation leads to circular imports between "managers.py" and
> > "models.py" for which I can't find a solution.
>
> > Assuming that I need to separate models and manager into different
> > files, so merging them is not an option, are there any best practices
> > or do you have any other suggestions?
>
> No expert on Django, and it's hard to tell what your GOAL is from this
> description, but you might want I'd write something like this:
>
> class Employee(models.Model):
> name = models.CharField(...)
>
> class Manager(Employee):
> manager_stuff...


I think the OP is talking about models.Managers:
http://docs.djangoproject.com/en/dev/topics/db/managers/

!-)

Polat Tuzla

unread,
Jan 6, 2009, 8:54:45 AM1/6/09
to Django users
Thank you very much for both of your responses. Local imports solved
my problem.
I had previously tried this without success, apparently there was
another mistake in the code.
Upon Bruno's suggestion I gave it another try, and it worked!

The reason why I need to separate models and managers into different
files is that they have simply grown to thousands of lines.

Regards,
Polat Tuzla


On Jan 6, 3:21 pm, bruno desthuilliers <bruno.desthuilli...@gmail.com>
wrote:

Polat Tuzla

unread,
Jan 6, 2009, 9:03:09 AM1/6/09
to Django users
Yes, indeed! It's the models.Manager I'm talking about.
I tried to keep my example short and easily comprehensible without
code snippets, but i think I've achieved the opposite.
Sorry for any inconvenience.

Polat Tuzla

On Jan 6, 3:37 pm, bruno desthuilliers <bruno.desthuilli...@gmail.com>
wrote:

bruno desthuilliers

unread,
Jan 6, 2009, 11:08:43 AM1/6/09
to Django users
on 6 jan, 14:54, Polat Tuzla <ptu...@gmail.com> wrote:
(snip)
> The reason why I need to separate models and managers into different
> files is that they have simply grown to thousands of lines.
>
It's indeed a very compelling reason !-)

But (simple suggestion - I know nothing about your project...) don't
you see it as a sign your app (django meaning) is getting a bit to
big ? Perhaps refactoring it into a couple or more small apps would be
a better move ? (once again, just my 2 cents...)

Ben Eliott

unread,
Jan 6, 2009, 1:45:48 PM1/6/09
to django...@googlegroups.com
Not that pretty/efficient but you could use contenttype contrib
temporarily.
IF ClassB has a ForeignKey to Class A could you extract the Class A
model from a foreign key via Class B's .meta?
Or the Class B could have a callable method which returned the
instance of the model you want...
Not sure whether/how these will work but just throwing out some ideas.

bruno desthuilliers

unread,
Jan 6, 2009, 2:46:16 PM1/6/09
to Django users
On 6 jan, 19:45, Ben Eliott <ben.dja...@googlemail.com> wrote:
> Not that pretty/efficient but you could use contenttype contrib
> temporarily.
> IF ClassB has a ForeignKey to Class A could you extract the Class A
> model from a foreign key via Class B's .meta?
> Or the Class B could have a callable method which returned the
> instance of the model you want...
> Not sure whether/how these will work but just throwing out some ideas.
>

This looks to me like dirty hacks that only add accidental complexity
instead of adressing the real problem. At least importing within a
method is a simple, well-known and idiomatic solution to circular
imports. The "correct" solution would be to turn models.py into a
package, split its content in submodules (with class A and B in
distinct submodules, each with their own manager), and use the
package's __init__.py to expose the submodule's Model classes at the
top-level, but IIRC this doesn't work well in Django (never had that
problem FWIW, since I tend to split my projects into a lot of small
apps).

Malcolm Tredinnick

unread,
Jan 6, 2009, 5:59:47 PM1/6/09
to django...@googlegroups.com
On Tue, 2009-01-06 at 04:42 -0800, Polat Tuzla wrote:
> Hi,
> Suppose I have two classes in "models.py", namely A and B. And there
> is the manager for B as BManager in "managers.py". BManager makes use
> of clas A.
>
> This situation leads to circular imports between "managers.py" and
> "models.py" for which I can't find a solution.

Since you don't tell us what you're doing currently, we'll have to break
out the crystal ball and see if the vapours let us guess as to the false
starts you might have made. It's hard to correct existing code without
knowing what it is. :-)

However, there shouldn't be any circular import problems. You just have
to remember to import only the module, not names inside the module in
one of the files. To wit...

In models.py:

from django.db import models

from managers import BManager

class A(models.Model):
...

class B(models.Model):
...
objects = BManager()

In managers.py:

from django.db import models

import models

class BManager(models.Manager):
...
# Refer to models.ModelA inside methods without problems.

When Python is importing models.py, it will see that it has to import
managers.py. So it does that, which says "import models". Python knows
that it has already imported (or is in the process of importing)
models.py, so it doesn't do it again, it just continues on. Providing it
does not need to refer to the contents of "models" (e.g. models.ModelA)
whilst parsing the managers module, there is no problem.

Circular import problems arise if you try to do "from models import
ModelA" in managers, because models isn't yet fully imported, so Python
cannot look up the ModelA inside that module. Problems would also arise
if you wrote something like this:

class BManager(models.Manager):
fred = models.ModelA.objects.all()

since the class definition is processed (executed) at parsing time,
which would require access to models.ModelA, which isn't available yet.
But that wouldn't be particularly sensible in any case (ModelA's
contents are dynamic, so reading it only at import time is inflexible).
If you have something like this:

class BManager(models.Manager):
def fred(self):
return models.ModelA.objects.all()

Then the statements inside fred() are not executed until the method is
called (they are parsed, but not executed, when the file is imported).
When you get around to calling method fred(), "models" will be fully
imported and there will be no problems.

>
> Assuming that I need to separate models and manager into different
> files, so merging them is not an option,

If you have some particularly twisted set up, then you'll simply need to
drop that assumption. It's not really a requirement for functionality,
merely a style issue. And sometimes style has to take a backseat to
practicality.

> are there any best practices

Best practice is "whatever works and is maintainable". People get far
too hung up on sticking a best practice label on things to the point
that it becomes devalued. Sure, some approaches look neater than others,
but I see too many people spinning their wheels wondering about the
unique "best" (in some truly debatable sense) solution, rather than just
getting on and solving their problems.

Regards,
Malcolm


Reply all
Reply to author
Forward
0 new messages