简体   繁体   中英

Multiple many-to-many relationships with a related_name of “+” result in the wrong query set

I have a Django model that has two many-to-many relationships to the auth.User class, like:

class Indicator(models.Model):
    friends = models.ManyToManyField(User, related_name="+")
    enemies = models.ManyToManyField(User, related_name="+")

The related_name is set to "+" so that there's no backwards relation , ie I don't need u.friends_set and u.enemies_set if u is a user.

Adding, removing, and clearing 'friends' and 'enemies' works fine - if I check directly in the database (ie not through Django) I can see the changes reflected as I would expect. However, if I get the query set through Django, I'm given the 'enemies' list whether I use i.friends.all() or i.enemies.all() (assuming i is an Indicator instance).

If I examine the ManyRelatedManager, I see that the through attribute is correct, which (if I understand correctly) is what allows add/remove/clear to work correctly:

>>> i.friends.through
<class 'project.app.models.Indicator_friends'>

However, the get_query_set method basically gets the superclass (the User Manager) and calls filter on it with kwargs i.friends.core_filters , which is {'+__pk': 404L} (if the Indicator ID is 404). The core_filters are the same for both "friends" and "enemies", which explains why I incorrectly get the same query set for both.

I can work around this (without setting a related_name): instead of i.friends.all() I can use:

[friend.user for friend in i.friends.through.objects.filter(indicator__id=i.id)]

However, that's hardly elegant.

  1. Is this a limitation of Django's "set related_name to '+'" system? (I couldn't find that documented anywhere) - ie that you can only use '+' once per distinct object/object pair?
  2. Is this a Django bug (ie something to report )?
  3. Is there a better way to work around this issue?

(Django 1.3 - I haven't tried trunk, but from reading the code it appears to have the same behaviour, with Python 2.7).

Looking at django/db/models/fields/related.py, it appears that the related_name = '+' option is really tested with a last character string slice in ManyToOneRel.is_hidden(). So simply appending a '+' to any string should do the trick, allowing you to have unique names in a single model and still have the backwards relationships ignored.

you could always set your own query to the database directly. I believe the queryset does in fact think the indicator are the same. I am not sure how friends and enemies interact, but perhaps you can just query there specific id's instead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM