#34207: Incorrect SQL query when adding a ManyToMany related object with a
"through" table prevents adding a new relationship if the new relationship
is identical except for a different value for "through_defaults"
-------------------------------------+-------------------------------------
Reporter: Credentive | Owner: nobody
Type: Bug | Status: closed
Component: Database layer | Version: 4.1
(models, ORM) |
Severity: Normal | Resolution: duplicate
Keywords: ManyToManyField | Triage Stage:
through | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Credentive):
I've re-read the section, and while it's not stated that it will work,
it's also not clear to me that it won't.
{{{
Now that you have set up your ManyToManyField to use your intermediary
model (Membership, in this case), you’re ready to start creating some
many-to-many relationships. You do this by creating instances of the
intermediate model:
>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
... date_joined=date(1962, 8, 16),
... invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()
<QuerySet [<Group: The Beatles>]>
>>> m2 = Membership.objects.create(person=paul, group=beatles,
... date_joined=date(1960, 8, 1),
... invite_reason="Wanted to form a band.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>
You can also use add(), create(), or set() to create relationships, as
long as you specify through_defaults for any required fields:
>>> beatles.members.add(john, through_defaults={'date_joined': date(1960,
8, 1)})
>>> beatles.members.create(name="George Harrison",
through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.set([john, paul, ringo, george],
through_defaults={'date_joined': date(1960, 8, 1)})
You may prefer to create instances of the intermediate model directly.
If the custom through table defined by the intermediate model does not
enforce uniqueness on the (model1, model2) pair, allowing multiple values,
the remove() call will remove all intermediate model instances:
>>> Membership.objects.create(person=ringo, group=beatles,
... date_joined=date(1968, 9, 4),
... invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo
Starr>]>
>>> # This deletes both of the intermediate model instances for Ringo
Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>
}}}
Looking at that text again, it is not clear that add() will not work for
multiple references to the same object, it's just suggested that adding
relationship objects directly is a matter of preference. It may be good to
clarify in the documentation that add does not support adding multiple
references to the same object. Would have saved me a few hours :)
In fact, all of the calls through the intermediate model kind of break
down, because you can't filter on attributes of the intermediate model in
the calls. I will be using the intermediate model for this, but actually
removing the manytomany relationship, as it doesn't really work. For
anyone who wants to create a self-referential relationship where the
values of the intermediate objects are intended to be used to filter the
relationship sets, the ManyToMany field won't support this, while the
intermediate objects themselves work fine. The only thing I could think of
that might make the work the way I want would be to write a custom
ManyRelatedManager, but I haven't seen any documentation on how to do
this, and it might not be worth the time just to get the ability to use
the manytomany methods.
If, in the example in the documentation, you wanted to query for the
members of the beatles as of a particular date, you would run into the
same problems I have. My whole use case is built around this, so I will
just use the intermediate table, and ignore all(), get(), add() etc.. The
use of the date in the example kind of suggests this could be possible
when in fact it isn't.
--
Ticket URL: <https://code.djangoproject.com/ticket/34207#comment:4>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/0107018505fe455e-af20fa87-2eb6-4514-8bb2-4e4bfc72c7a2-000000%40eu-central-1.amazonses.com.