views:

944

answers:

4

I have two models: Play and PlayParticipant, defined (in part) as:

class PlayParticipant(models.Model):
    player = models.ForeignKey('Player')
    play = models.ForeignKey('Play')
    note = models.CharField(max_length=100, blank=True)

A piece of my code has a play p which has id 8581, and I'd like to add participants to it. I'm trying to use RelatedManager's .create() to do that, like:

p.playparticipant_set.create(player_id=2383)

From which, Django constructs:

INSERT INTO `api_playparticipant` (`player_id`, `play_id`, `note`) VALUES (2383, 2383, '')

Is this a bug in Django, or am I misusing .create()?

Copy-pasting shell for sanity check:

In [17]: p = Play.objects.get(id=8581)

In [18]: p.id
Out[18]: 8581L

In [19]: p.playparticipant_set.create(player_id=2383)
...
IntegrityError: (1452, 'Cannot add or update a child row: a foreign key constraint fails (`gc/api_playparticipant`, CONSTRAINT `play_id_refs_id_60804ffd462e0029` FOREIGN KEY (`play_id`) REFERENCES `api_play` (`id`))')

From query.log:

5572 Query       INSERT INTO `api_playparticipant` (`player_id`, `play_id`, `note`) VALUES (2383, 2383, '')
+1  A: 

You should be using a ManyToManyField in the Play model that relates to the zero or more participants. In that code you would just be doing

play.players.add(player)
ironfroggy
Meh, can't as there's an extra field - a note, in the relationship (updated question to include it). Example is identical to: http://www.djangoproject.com/documentation/models/m2m_intermediary/
Andrey Fedorov
Also, I'd like to figure out what the bug *is* rather than work around it (which I did with PlayParticipant.objects.create(...))
Andrey Fedorov
A: 

It is not a bug; check the documentation; specifically the section on Extra fields on many-to-many relationships. It specifies:

Unlike normal many-to-many fields, you can't use add, create, or assignment (i.e., beatles.members = [...]) to create relationships.

The only way to create this type of relationship is to create instances of the intermediate model.

Wogan
That is not a many-to-many relationship, esp. not a many-to-many with properties, but a simple one-to-many; the documentation says about intermediate model for many-to-many relationships having properties; parts you quoted are completely unrelated to the question.
Maciej Pasternacki
+3  A: 

I don't see how is your first example supposed to be a bug? This behavior is completely intuitive. Your p is a play with an id of 2383, and you're calling the create method on its related set. You're also specifying an extra field, called *player_id*, and giving it a value of 2383. It's only logical that both play id will be 2383 (because that's the id of the play containing this related set) and that player id will also be 2383 (because you explicitly passed that value in).

Your second example would seem to indicate a bug, but I cannot reproduce it. Here are my models:

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

class Play(models.Model):
    title = models.CharField(max_length=100)

class PlayParticipant(models.Model):
    player = models.ForeignKey('Player')
    play = models.ForeignKey('Play')
    note = models.CharField(max_length=100, blank=True)

Here's the shell output:

>>> player1 = Player.objects.create()
>>> player2 = Player.objects.create()
>>> player1.id
2
>>> player2.id
3
>>> play1 = Play.objects.create()
>>> play2 = Play.objects.create()
>>> play1.id
3
>>> play2.id
4
>>> pp1 = play1.playparticipant_set.create(player_id=2)
>>> pp1.play.id
3
>>> pp1.player.id
2

In any case, why are you posting this to SO? Django has a bugtracker, a couple of active official mailing lists and a couple of IRC channels.

oggy
In his output he shows the Play id is 8581
ironfroggy
But his post says: "A piece of my code has a play p which has id 2383".
oggy
+1 for suggestion to use Django's bugtracker
Piotr Czapla
@oggy: this was a typo, corrected. sorry :-/ what version of Django did you use?
Andrey Fedorov
Tested on both trunk and 1.0.2, with sqlite3 backend.
oggy
I was running Django 1.0.2 at the time with MySQL 5.0.81... will reproduce by itself when I get the chance, and file bug report if it's still reproducible.
Andrey Fedorov
Thanks for all the help, oggy :)
Andrey Fedorov
A: 

I cannot reproduce that behaviour. With similar models, I can run:

m = Manufacturer.objects.get(1)
p = m.product_set.create(name='23',category_id=1,price=23,delivery_cost=2)

and I get new instance p of Product, whose p.manufacturer foreign key equals m.

This is on Django-1.0.2 with PostgreSQL; which version of Django do you use, and which SQL backend?

Try to use two new minimal models with ForeignKey field on one of them, which should work; then make these models gradually closer to models that exhibit the failing behaviour. This way you can isolate single change that makes it misbehave; this change is key to the reason.

Maciej Pasternacki