Model A imports Model B imports Model A - I'm completely lost as to how to get around circular imports

6 views
Skip to first unread message

codecowboy

unread,
Apr 5, 2009, 10:43:13 AM4/5/09
to Django users
Hi Everyone,

I'm new to the Django community and I am having trouble with circular
imports. I've read every article that I can find about the issue
including posts on this group. I'm going to paste my model files and
the stack trace. I'm sure that this must be an issue that has come up
before. Thank you in advance for any help. If you know of an article
that explains the problem then point me to it. Thanks again.

------ Model A -----------

from django.db import models
from scinet.conferences.models import ConferenceAttendee
class Scientist(models.Model):
user = models.ForeignKey('auth.User')
summary = models.TextField()
undergrad = models.CharField(max_length=50)
graduate = models.CharField(max_length=50)
post_doctorate = models.CharField(max_length=50)
current_employer = models.CharField(max_length=50)
zipcode = models.CharField(max_length=5)
city = models.CharField(max_length=50)
state = models.CharField(max_length=2)
country = models.CharField(max_length=200, blank=False)
created = models.DateTimeField()
modified = models.DateTimeField()
conferences = models.ManyToManyField(Conference,
through='ConferenceAttendee')

------ Model B -----------

from django.db import models
from scinet.scientists.models import Scientist
class Conference(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
date = models.DateTimeField()
scientists = models.ManyToManyField(Scientist,
through='ConferenceAttendee')
created = models.DateTimeField()
modified = models.DateTimeField()

def __unicode__(self):
return self.name

class ConferenceAttendee(models.Model):
conference = models.ForeignKey(Conference)
scientist = models.ForeignKey(Scientist)
rsvp_date = models.DateTimeField()
presenter = models.BooleanField()

------ Stack Trace ------------

Validating models...
Unhandled exception in thread started by <function inner_run at
0x84f6aac>
Traceback (most recent call last):
File "/usr/lib/python2.5/site-packages/django/core/management/
commands/runserver.py", line 48, in inner_run
self.validate(display_num_errors=True)
File "/usr/lib/python2.5/site-packages/django/core/management/
base.py", line 246, in validate
num_errors = get_validation_errors(s, app)
File "/usr/lib/python2.5/site-packages/django/core/management/
validation.py", line 28, in get_validation_errors
for (app_name, error) in get_app_errors().items():
File "/usr/lib/python2.5/site-packages/django/db/models/loading.py",
line 128, in get_app_errors
self._populate()
File "/usr/lib/python2.5/site-packages/django/db/models/loading.py",
line 57, in _populate
self.load_app(app_name, True)
File "/usr/lib/python2.5/site-packages/django/db/models/loading.py",
line 72, in load_app
mod = __import__(app_name, {}, {}, ['models'])
File "/home/guy/DjangoApps/scinet/../scinet/scientists/models.py",
line 2, in <module>
from scinet.conferences.models import ConferenceAttendee
File "/home/guy/DjangoApps/scinet/../scinet/conferences/models.py",
line 2, in <module>
from scinet.scientists.models import Scientist
ImportError: cannot import name Scientist

Ramiro Morales

unread,
Apr 5, 2009, 11:19:32 AM4/5/09
to django...@googlegroups.com
On Sun, Apr 5, 2009 at 11:43 AM, codecowboy <guy....@gmail.com> wrote:
>
> Hi Everyone,
>
> I'm new to the Django community and I am having trouble with circular
> imports.  I've read every article that I can find about the issue
> including posts on this group.  I'm going to paste my model files and
> the stack trace.  I'm sure that this must be an issue that has come up
> before.  Thank you in advance for any help.  If you know of an article
> that explains the problem then point me to it.  Thanks again.
>
> [snip]

Do you need to have two, parallel, many to many relationships between Scientist
and Conference?. If the answer is no then yo don't need to define that
relationship in both models. This alone almost solves your circular reference
problem.

The technique of using a string with the name of the model instead of tthe Model
class onject iself to indicate the target of a relationship is only needed (and
accepted) when:

* The target model hasn't yet been defined (It comes after in the same
application models.py file).

* or when you have two mutually referencing relationships between
two models located in diferent applications (actually, this second use case
isn't clearly explained in our documentation)

Using these guidelines and simplifying you example to the relevant bits,
something like this could be a start of a solution to your problem:

------ scinet.scientists.models -----------
from django.db import models

class Scientist(models.Model):
# ...
# no many to many field needed here

------ scinet.conferences.models -----------


from django.db import models
from scinet.scientists.models import Scientist

class ConferenceAttendee(models.Model):
# ...
conference = models.ForeignKey('Conference')
scientist = models.ForeignKey(Scientist)

class Conference(models.Model):
# ...
scientists = models.ManyToManyField(Scientist, through=ConferenceAttendee)


Relevant documentation links worth reading:

http://docs.djangoproject.com/en/dev/topics/db/models/#many-to-many-relationships
http://docs.djangoproject.com/en/dev/topics/db/models/#models-across-files
http://docs.djangoproject.com/en/dev/ref/models/fields/#lazy-relationships

HTH

--
Ramiro Morales
http://rmorales.net

codecowboy

unread,
Apr 5, 2009, 12:56:55 PM4/5/09
to Django users
Hi Ramiro,

Thank you for the response. I read the links and modified my model
files as you instructed. I am currently able to loop through
scientists that belong to a conference and print them out. However, I
also need to be able to loop through conferences that a scientist is
attending. I am not able to do that with the solution that you
provided.

------ myproject.scientist.views -----
def portal
s = get_object_or_404(Scientist, pk=scientist_id)

----- templates/portal.html
{% for conference in scientist.conference.all %}
<a href="/">{{ conference.title }}</a>,
{% endfor %}

On Apr 5, 11:19 am, Ramiro Morales <cra...@gmail.com> wrote:
> http://docs.djangoproject.com/en/dev/topics/db/models/#many-to-many-r...http://docs.djangoproject.com/en/dev/topics/db/models/#models-across-...http://docs.djangoproject.com/en/dev/ref/models/fields/#lazy-relation...
>
> HTH
>
> --
> Ramiro Moraleshttp://rmorales.net

Doug B

unread,
Apr 5, 2009, 1:08:03 PM4/5/09
to Django users

codecowboy

unread,
Apr 5, 2009, 1:57:34 PM4/5/09
to Django users
Doug,

Thank you so much. I completely missed that detail while ripping
through the django docs. I've got it all working now. Ramiro, thanks
again for your help.

For anyone else who is trying to do this and stumbles upon this
thread, here is my full final solution.

--- Overview ---
I've got two applications within the scinet project.
scinet.conferences and scinet.scientists There are 4 files in all
that I need for this to work. scinet.conferences.models.py,
scinet.scientists.models.py, scinet.scientists.views.py and
scinet.templates.scientists.portal.html

--- scinet.conferences.models (DjangoProjects/scinet/conferences/
models.py) ---

I define the many-to-many relationship between conferences and
scientists here. A conference can be attended by multiple scientists
and a scientist can attend multiple conferences. Do note that I have
arbitrarily chosen to use the Conference model to specify the
relationship between Conferences and Scientists. I could have done
this in my scientist model just as well. It is important to not that
I had to import the Scientist model here since I must reference it in
order to specify the relationship in the following line: scientists =
models.ManyToManyField(Scientist, through='ConferenceAttendee').

from django.db import models
from scinet.scientists.models import Scientist

class Conference(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
date = models.DateTimeField()
scientists = models.ManyToManyField(Scientist,
through='ConferenceAttendee')
created = models.DateTimeField()
modified = models.DateTimeField()

def __unicode__(self):
return self.name

class ConferenceAttendee(models.Model):
conference = models.ForeignKey(Conference)
scientist = models.ForeignKey(Scientist)
rsvp_date = models.DateTimeField()
presenter = models.BooleanField()

--- scinet.scientists.models (DjangoProjects/scinet/scientists/
models.py) ---

Here is where I define my Scientist model. The point to note here is
that I do NOT need to reference the conference model because in
Django, I only need to define a relationship between two models in one
of the two models. In fact, you must ONLY define the relationship
between two models in ONE of the two models. DO NOT SPECIFY A
RELATIONSHIP BETWEEN TWO MODELS IN BOTH MODELS.

from django.db import models

class Scientist(models.Model):
user = models.ForeignKey('auth.User')
summary = models.TextField()
undergrad = models.CharField(max_length=50)
graduate = models.CharField(max_length=50)
post_doctorate = models.CharField(max_length=50)
current_employer = models.CharField(max_length=50)
zipcode = models.CharField(max_length=5)
city = models.CharField(max_length=50)
state = models.CharField(max_length=2)
country = models.CharField(max_length=200, blank=False)
created = models.DateTimeField()
modified = models.DateTimeField()

--- scinet.scientists.views (DjangoProjects/scinet/scientists/
views.py) ---

There is nothing special to note here other than that I am simply
getting a scientist. The following line grabs a scientist and all
related conferences. s = get_object_or_404(Scientist,
pk=scientist_id). In the template file which I will show next, you
will see how I access the conferences that are associated with this
particular scientist. Do note once again that there is no need to
explicitly import the Conference model. Django (or Python) does this
magically (which I am curious about if anyone is up for explaining
that to me).

from django.shortcuts import render_to_response, get_object_or_404
from django.template import Context, loader
from scinet.scientists.models import Scientist
from django.http import HttpResponse, HttpResponseRedirect

def portal(request):
scientist_id = 1
s = get_object_or_404(Scientist, pk=scientist_id)
return render_to_response('scientists/portal.html',
{'scientist':s})

--- scinet.templates.scientists.portal (DjangoProjects/scinet/
templates/scientists/portal.html) ---

The key here is to understand how to traverse related models in
reverse. Check out the link that Doug posted above. In short,
because I chose to indicate a relationship between Conferences and
Scientists in the Conference model, Scientist must access conferences
in reverse. I used Django's default naming of a reverse model
reference in my loop below. In case you don't know, here is the loop
for traversing scientists that belong to a conference.

{% for scientist in conference.scientists.all %}
<li><a href="#">{{ scientist.user.get_full_name }}</a></li>
{% endfor %}

In order to traverse the conferences that a scientist will attend you
must use scientists.conference_set.all. The key differnece being
"_set". Anyways, the template code that I used is below.

<h1>{{ scientist.user.get_full_name }}</h1>
<h2>You are attending the following conferences</h2>

{% for conference in scientist.conference_set.all %}
<a href="/">{{ conference.title }}</a>,
{% endfor %}

Thanks again everyone. I hope that this helps someone else.

Thank you,

Guy

On Apr 5, 1:08 pm, Doug B <dball...@gmail.com> wrote:
> Take a look at this part of the documentation:
>
> http://docs.djangoproject.com/en/dev/topics/db/queries/#backwards-rel...
Reply all
Reply to author
Forward
0 new messages