Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
3.5k views
in Technique[技术] by (71.8m points)

python - Django filter a prefetched queryset returning an empty list

In my app I have some Users and Groups, that are related by a many to many relation through GroupMember.

class User(models.Model):
    ...

class Group(models.Model):
    ...
    users = models.ManyToManyField(User, through='groups.GroupMember', related_name='groups')


class GroupMember(models.Model):
    ...
    user = models.ForeignKey(User, related_name='group_members', on_delete=models.CASCADE)
    group = models.ForeignKey(Group, related_name='members', on_delete=models.CASCADE)

Now I am trying to build an endpoint that retrieves a list of user instances that are in any one of the groups that your user is in. So if user A and B are in group1, user A and C are in group2, and C and D are in group3, retrieving the users as A would return B and C (since those are in groups with A).

That filter part can be done in multiple ways, but the part where I am stuck is how to show which groups were the cause of the relation. I tried to use prefetch_related with a custom filter, to fetch only the groups that have user A in them, like this:

User.objects.exclude(id=user.id).annotate(
    # Annotate the amount of groups related between the users
    related_group_count=Count('groups', filter=Q(groups__users=user))
).filter(
    # Only users that have at least 1 relation with this user
    related_group_count__gt=0
).prefetch_related(
    # Prefetch the specific groups to show
    Prefetch('groups', User.groups.all(), to_attr='related_groups'),
)

The returned users will not have any related_groups, it will be an empty qs. When I include the request user (by removing the exclude part on the query), the groups will be available for that user, but still not any other. This is my current response (including the requesting user "A"):

{
    "first_name": "A",
    "related_groups": [
        {
            "name": "group1",
        },
        {
            "name": "group2",
        }
    ]
},
{
    "first_name": "B",
    "related_groups": []
},
{
    "first_name": "C",
    "related_groups": []
}

But I'm trying to achieve this:

{
    "first_name": "B",
    "related_groups": [
        {
            "name": "group1",
        }
    ]
},
{
    "first_name": "C",
    "related_groups": [
        {
            "name": "group2",
        }
    ]
}

The problem lies with the filter on the Prefetch queryset, but I can't figure out what I'm doing wrong or if this is even possible. I tried different way of filtering (also with Group.objects.filter(users=user)) or even annotating the group members, counting if the given user is in that group and filtering on that, but with no success...


Note: This only happens when filtering the prefetch as described above: User (is group member) -> Group -> User (group member). It looks like the filter is "overwriting" the user in the queryset with the initial user (A).

In my project I work with multiple group-like concepts, one of which has 2 different kind of members (lets say, in context of this question a second GroupMember type model, with an additional many to many relation in the group model to user). When using User (group member) -> Group -> User (other kind of member), the prefetch actually works as expected. This issue is only when prefetching using the same through-model in relation to the user.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I managed to get this to work, by using a separate queryset that gets the group ids. This way the prefetch filter will not use the requesting user id to filter, which resulted in empty lists for all other users.

group_ids = user.groups.values('pk')

User.objects.exclude(id=user.id).filter(
    groups__in=group_ids
).prefetch_related(
    Prefetch('groups', Group.objects.filter(id__in=group_ids), to_attr='related_groups'),
)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...