ForeignKey problem

86 views
Skip to first unread message

Soviet

unread,
Jun 25, 2012, 12:41:14 PM6/25/12
to Django users
Hey
I'm new to this Django thing and I run into first problem :).

Let's say I have two models and in each I have field with ForeignKey
relating to field in other model (hope it's clear). Now that I want to
run migrate with South, I'm getting "NameError: name 'ModelName' is
not defined". This is clearly (?) an issue with the fact that one
model is on top of the other one and second Model hasn't been defined
yet. Changing position of the models is, obviously, not helping. How
do I approach this?

Thanks up front for all the help!

Adrian Bool

unread,
Jun 25, 2012, 12:43:28 PM6/25/12
to django...@googlegroups.com
In the first of the two models, use a string containing the second model's name as the first argument to the ForeignKey method.

Regards,

aid

Soviet

unread,
Jun 25, 2012, 12:53:00 PM6/25/12
to Django users
Thank you kind sir for your fast response, that worked brilliantly.
Can I be cheeky and ask why does it work? :)

Kurtis Mullins

unread,
Jun 25, 2012, 12:54:48 PM6/25/12
to django...@googlegroups.com
[...]

> Let's say I have two models and in each I have field with ForeignKey
> relating to field in other model (hope it's clear). [...]

Actually, I'm pretty confused about this part :) A ForeignKey is used to relate to another Model -- not just a Model Field -- in Django's ORM.

So for example, if you have a Team Model and a Player Model, you'd want to include "team = models.ForeignKey(Team)" in your Player Model. If you want to back-reference this from your Team, this is also possible (just look through the docs). You don't need to declare the ForeignKey relationship twice. Finally, when you want a specific field from the other Model (for example, maybe a team's name) then you'd just query accordingly: my_player.team.name

Adrian Bool

unread,
Jun 25, 2012, 1:02:40 PM6/25/12
to django...@googlegroups.com

On 25 Jun 2012, at 17:53, Soviet wrote:

> Thank you kind sir for your fast response, that worked brilliantly.
> Can I be cheeky and ask why does it work? :)

Magic! ;-)

Although seriously, Django obviously has some code in there to handle just the situation you have come across. Sorry, I don't have a deeper answer than that!

Cheers,

aid


Thomas Lockhart

unread,
Jun 25, 2012, 1:07:47 PM6/25/12
to django...@googlegroups.com
On 12-06-25 10:02 AM, Adrian Bool wrote:
> On 25 Jun 2012, at 17:53, Soviet wrote:
>
>> Thank you kind sir for your fast response, that worked brilliantly.
>> Can I be cheeky and ask why does it work? :)
> Magic! ;-)
>
The mechanism must involve deferred resolution of the second model by
passing the model class name in as a string. Without string syntax
python insists on knowing what that class is at the time it sees the
reference. Don't know more than that though ;)

- Tom

Kurtis Mullins

unread,
Jun 25, 2012, 2:29:26 PM6/25/12
to django...@googlegroups.com

The mechanism must involve deferred resolution of the second model by passing the model class name in as a string. Without string syntax python insists on knowing what that class is at the time it sees the reference. Don't know more than that though ;)


Soviet

unread,
Jun 25, 2012, 4:36:09 PM6/25/12
to django...@googlegroups.com
W dniu poniedziałek, 25 czerwca 2012 18:54:48 UTC+2 użytkownik Kurtis napisał:
Actually, I'm pretty confused about this part :) A ForeignKey is used to relate to another Model -- not just a Model Field -- in Django's ORM.

So for example, if you have a Team Model and a Player Model, you'd want to include "team = models.ForeignKey(Team)" in your Player Model. If you want to back-reference this from your Team, this is also possible (just look through the docs). You don't need to declare the ForeignKey relationship twice. Finally, when you want a specific field from the other Model (for example, maybe a team's name) then you'd just query accordingly: my_player.team.name

Let me follow up on this. Say I want to add list of all Teams my Players played for. What you're saying is that I don't have to add ForeignKey to Team and just use team_name field from Team model? Will it work?

This relations stuff is confusing :P.

Kurtis Mullins

unread,
Jun 25, 2012, 5:16:28 PM6/25/12
to django...@googlegroups.com

Let me follow up on this. Say I want to add list of all Teams my Players played for. What you're saying is that I don't have to add ForeignKey to Team and just use team_name field from Team model? Will it work?

This relations stuff is confusing :P.

Haha, no problem! It'll come natural after a while.

Let's start out with the organization of this. There's really two main relationships you will deal with. 

Foreign Keys

The first one is ForeignKeys, These are generally used when you want to do what's called a "One to Many" relationship.

So, for example, let's say you have several teams and several players. We could assume that each player is only playing for one team at any given time. This means that you will not find a single player playing for two times. Under this assumption, you would have a "One to Many" relationship because one team will have many players. However, each player will only have one team. That may sound confusing -- read it over a few times if it does :) Anyways, this scenario would best be solved by using a ForeignKey.

Here's a little visualization just in case that is confusing:

Team: The Bengals
Players: John, Bob, Tim, Joe

Team: The Steelers
Players: Adam, Chris, Frank, Steve

Notice that each player is only playing for one Team. Each team, however, has multiple players. In this situation, your models would be setup like this:

class Team(models.Model):
    name = models.CharField(max_length=50)

class Player(models.Model):
    name = models.CharField(max_length=50)
    team = models.ForeignKey(Team)

Now, let's say you want to grab some information about each of these.

1. We have the player Joe, how do we get his team?

>> "The Bengals"

2. We have the team "The Steelers", how do we get the list of players?

print steelers.player_set.all()
>> "Adam", "Chris", "Frank", "Steve"

Many to Many

The other common type of relationship you'll run into is called a Many to Many relationship. This relationship is, essentially, two Models that can have relationships with many objects from both sides of the relationship. So to help you understand and visualize how this works, we're going to take the previous example and modify it a bit.

Let's say we have Two Teams and Several People. However, now people are allowed to play for Multiple teams. In this example, assume that each player with the same name is the same person (each name is unique).

Team: The Bengals
Players: Adam, Bob, Charlie

Team: The Steelers
Players: Donald, Evan, Adam

Note: Adam plays for both teams.

So, looking from Adam's perspective, we see that he plays for both the Steelers and the Bengals. That is -- he has a "many" relationship with the Teams. Looking at the players from the Team's perspective, it's obvious that each team has many players. Therefore, in this relationship it is okay to have Many-to-Many objects from both sides. 

Here's the basic Model setup for this:

class Team(models.Model):
    name = models.CharField(max_length=50)

class Player(models.Model):
    name = CharField(max_length=50)
    teams = models.ManyToManyField(Team)

Let's do a couple of examples to see where this is different.

1. Just like before, we have a player but we want to see his Team:

print donald.teams.all()
>> "The Steelers"

2. Now, like before, we want to see this player's team. However, the Player is no longer restricted to a Single Team.

print adam.teams.all()
>> "The Bengals", "The Steelers"

3. Let's get the reverse relationships -- Who all plays for the Steelers?

print steelers.player_set.all()
>> "Donald", "Evan", "Adam"

So, hopefully, from that example you can see that not much has changed from the perspective of the Team. It already had a "many" relationship with the players. However, the thing that has changed here is that the players now have a "many" relationship with the Teams.

And to answer your question directly about including the field in the Team:
No, you don't need to put a ForeignKey in each Model. You just put it in once. This will automatically create a variable in your other Model called <ModelName>_set which can be used to do queries/lookups. Please note that you can also do something like this to make the naming more practical and easier to read:

class Team(models.Model)
    name = models.CharField(...)

class Player(models.Model)
    name = models.CharField(...)
    teams = models.ManyToManyField(Team, related_name='players')

Then, you can simply do this: 
myteam.players.all() # Notice the word "set" is no longer in there.

Hopefully that helps a bit and I didn't just confuse you more :) Let us know if you run into any more problems and good luck!
 

Message has been deleted

Soviet

unread,
Jul 5, 2012, 12:45:22 PM7/5/12
to django...@googlegroups.com
Thanks a lot! After few experiments I think I get it :).

But! I do have another problem. Lets ditch our football example. Let's say that I have something like that:

class CherryTree(models.Model):
    name = models.IntegerField()
    cherries = models.ManyToManyField('CherryFruit')

class CherryFruit(models.Model):
    name = models.CharField(max_length=50)

More or less. The point is, I want to ass many CherryFruit to one CherryTree. How do I pass the additional information? I suspect this can have something to do with the 'through' argument, but I'm probably completely wrong. Or do I have to create additional Model, like:

class CherriesOnTree(models.Model):
    name = models.ForeignKey('CherryFruit')
    tree = models.ForeignKey('CherryTree')
    amount = models.IntegerField()

and add each fruit separately? Hope It's clear what I mean :).

Tomas Neme

unread,
Jul 5, 2012, 1:40:06 PM7/5/12
to django...@googlegroups.com
> But! I do have another problem. Lets ditch our football example. Let's say
> that I have something like that:
>
> class CherryTree(models.Model):
>     name = models.IntegerField()
>     cherries = models.ManyToManyField('CherryFruit')
>
> class CherryFruit(models.Model):
>     name = models.CharField(max_length=50)
>
> More or less. The point is, I want to ass many CherryFruit to one
> CherryTree. How do I pass the additional information? I suspect this can
> have something to do with the 'through' argument, but I'm probably
> completely wrong. Or do I have to create additional Model, like:
>
> class CherriesOnTree(models.Model):
>     name = models.ForeignKey('CherryFruit')
>     tree = models.ForeignKey('CherryTree')
>     amount = models.IntegerField()
>
> and add each fruit separately? Hope It's clear what I mean :).

well, no, no and no, but interesting anyways.

A tree and it's fruit is the same as a team and it's players.

You'd do


class CherryTree(models.Model):
    name = models.IntegerField()

class CherryFruit(models.Model):
    tree = models.ForeignKey(CherryTree)
    name = models.CharField(max_length=50)

because each cherry belongs to a single tree. You can think of it as a parent-child relationship, the children are the ones that have the ForeignKey.

The "through" parameter in a many to many just defines the name of the table to be used for that "CherriesOnTree" model you put there, which you wouldn't need to define. Also, the "amount" field would be of no point, would it?

then you would do this:

# create a new tree
my_tree = Tree("Tree on the corner")
# or maybe get a tree from the database
my_tree = Tree.objects.get(name="Tree on the corner")

# create a new cherry for this tree
cherry = Cherry(tree=my_tree, name="Some Cherry Name")
cherry.save()
## or if you don't need to do anything afterwards, you can simply
Cherry(tree=my_tree, name="Some Cherry Name").save()

a many-to-many would be used, for example, in a classes and students scenario

Each student goes to many classes, each class has many students, so:

class Class(models.Model):
   name = models.CharField(max_length=50)

class Student(models.Model):
   name = models.CharField(max_length=50)
   classes = models.ManyToManyField(Class)

# create class
math = Class(name="Math")
math.save()

# create student
student = Student(name="Charles")
student.save() # need to save before setting up many to manies
# Charles goes to Math class
student.classes.add(math)

Here an extra table will be created to hold the many-to-many relationships. I think it's called <appname>_class_m2m_student, or something like that.

The through class is used if you want to add some extra data, like for example, where does a student seat in a particular class, or what other students he's in groups with in there, see

https://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany

for more details

--
"The whole of Japan is pure invention. There is no such country, there are no such people" --Oscar Wilde

|_|0|_|
|_|_|0|
|0|0|0|

(\__/)
(='.'=)This is Bunny. Copy and paste bunny
(")_(") to help him gain world domination.

Soviet

unread,
Jul 6, 2012, 9:17:32 AM7/6/12
to django...@googlegroups.com
Alright, bad example.

Lets try LEGO pieces. I have a LEGO set that has many pieces (ManyToMany). But I have 4 wheels and 1 brick in that set. The 'brick' can be added with no problem, but how do I save the information that there's 4 'wheel' objects and not only one?

Melvyn Sopacua

unread,
Jul 6, 2012, 9:52:08 AM7/6/12
to django...@googlegroups.com
On 6-7-2012 15:17, Soviet wrote:

> Lets try LEGO pieces. I have a LEGO set that has many pieces (ManyToMany).
> But I have 4 wheels and 1 brick in that set. The 'brick' can be added with
> no problem, but how do I save the information that there's 4 'wheel'
> objects and not only one?

You don't. You get all wheel objects and count them. However, I think
the through argument is best explained with your original question, when
you asked:
"Say I want to add list of all Teams my Players played for."

This does not change the player model, nor the team model. What you're
asking for here, is a new model "player history", which is defines the
many-to-many:

class Team(models.Model) :
name = models.CharField(max_length=64)

class Player(models.Model) :
name = models.CharField(max_length=64)
playing_for = models.ForeignKey(Team, related_name='players')
history = models.ManyToManyField(Team, related_name='player_histories',
through='PlayerHistory') #quoted!

class PlayerHistory(models.Model) :
team = models.ForeignKey(Team)
player = models.ForeignKey(Player)
started = models.DateField()
ended = models.DateField()


--
Melvyn Sopacua


Soviet

unread,
Jul 6, 2012, 10:23:44 AM7/6/12
to django...@googlegroups.com

You don't. You get all wheel objects and count them.

I don't understand. It's the same wheel added four times, not four different wheels.
 

Tomas Neme

unread,
Jul 6, 2012, 10:43:27 AM7/6/12
to django...@googlegroups.com
> I don't understand. It's the same wheel added four times, not four
> different wheels.

I guess you could implement it either way. The thing is that doing it
this way would become complicated at the time you need to define what
is attached where. For Lego pieces, I'd do this:

class LegoType(Model) has a description, maybe a picture, and it's behavior

model LegoPiece has a ForeignKey(LegoType), a ManyToMany to other
LegoPieces (the ones it's connected to).

You'd have one LegoType = wheel, and four LegoPiece who's type would
be wheel. When you defined the Type you'd need a way to link each
model to specific python code that controlled how many pieces it can
connect to, and how, but that's a song for another day.

Then you do LegoPiece.objects.filter(type__name="wheel").count(), and
voila, you have how many wheels you have.

You COULD have enough knowing the amount of pieces of each kind you
have, you'd be modeling something similar to an inventory, but
defining connections in that context would be unnecessarily
complicated, since you'd not only have to count how many pieces you
have of each type, but when you connect one to another, *which one*
are you connecting? one that's free, or one that's connected to
another one? How would you model the connections? Say one brick can
connect to as many as 8 other pieces, 4 on each side, say you have 3
bricks, so you know you can't have more than 24 connecctions, but..
how do you differentiate between A--B C--D--E and A--B--C--D or.. I
don't know how to draw it, but D--A--B D--A--C (A is connected to D on
one side, and to B and C both on the other).

It might be that you can model your needs this way, but AFAICS, it'd
be a lot harder to follow things and keep things consistent in an
Object Oriented way.

Soviet

unread,
Jul 6, 2012, 1:14:53 PM7/6/12
to django...@googlegroups.com
Thanks, but damn, isn't that overly complicated (for me). Using through looks like a easier way, but then I have to add Wheel four times and can't just type '4' in some field, since I don't need it to exist in database four time, just an information that there are four wheels.

Soviet

unread,
Jul 6, 2012, 1:26:24 PM7/6/12
to django...@googlegroups.com
If I'll do something like:

class Car(models.Model):
    name = models.CharField(max_length=100)
    parts = models.ManyToManyField('Part', through = 'PartsInCar')

class Part(models.Model):
    name = models.CharField(max_length=100)

class PartsInCar(models.Model):
    car = models.ForeignKey('Car')
    part = models.ForeignKey('Part')
    amount = models.IntegerField()

Whats the advantage of doing it 'through' if I can just make it a new model? Or am I completely wrong about everything again? :)

(Sorry for the double post)

Thomas Lockhart

unread,
Jul 6, 2012, 1:27:01 PM7/6/12
to django...@googlegroups.com
On 12-07-06 10:14 AM, Soviet wrote:
> Thanks, but damn, isn't that overly complicated (for me). Using
> through looks like a easier way, but then I have to add Wheel four
> times and can't just type '4' in some field, since I don't need it to
> exist in database four time, just an information that there are four
> wheels.
This is Legos. At some point you will only have 3 wheels because the dog
ate the fourth one...

- Tom

Tomas Neme

unread,
Jul 6, 2012, 1:32:42 PM7/6/12
to django...@googlegroups.com
> Thanks, but damn, isn't that overly complicated (for me). Using through
> looks like a easier way, but then I have to add Wheel four times and can't
> just type '4' in some field, since I don't need it to exist in database four
> time, just an information that there are four wheels.

as I said, it depends on what you want. If you want an inventory
(different types of blocks, and how many you have of each) then it's
good enough that way. If you want to model actually building stuff
with legos, it might not be enough, that's all I'm saying.

For an inventory I'd do this:

class LegoPiece(Model) has it's data, plus an IntegerField, total amount..

As I said before, with your car example.. if you just want to know how
many of a part are in a car, Then what you did is enough, then you
could have some cars, and some pieces, say

Cars = (Motorcycle, 4x4, Car)
Parts = (Tire, Transmission, Door)
PartsInCar = ((Motorcycle, Tire, 2),(Motorcycle, Transmission, 1),
(4x4, Tire, 4), (4x4, Transmission, 2), (4x4, Door, 2), (Car, Tire,
4), (Car, Transmission, 1), (Car, Door, 4))

This would achieve a description of how may parts you need to make a given car..
Message has been deleted

Tomas Neme

unread,
Jul 6, 2012, 1:39:31 PM7/6/12
to django...@googlegroups.com
oh, and the advantage is that you could have a car, for example

car = Car.objects.get(name="4x4")

car.parts.get(name="Transmission").partsincar.amount >> 2

The difference with doing it with another model is that you cannot do
this, for example (or not as easily)

Car.objects.filter(parts__name="Transmission", partsincar__amount=1)
>> Motorcycle + Car
Message has been deleted

Kurtis Mullins

unread,
Jul 16, 2012, 4:16:31 PM7/16/12
to django...@googlegroups.com
I'd suggest taking a step back from the data structure of your application, decide what you want to do with your data, and then take another stab at it. 

If you have more questions, I'd recommend posting your questions using your application's real domain instead of continuing to use "similar" ideas.

Good luck!
Reply all
Reply to author
Forward
0 new messages