【问题标题】:How to filter all objects that contain multiple elements in a many-to-many relationship using Django's ORM?如何使用Django的ORM过滤包含多对多关系中的多个元素的所有对象?
【发布时间】:2018-09-09 00:54:00
【问题描述】:

我在 Django 中有两个类通过 ManyToManyField 链接(User 类是内置的 User 模型):

from django.contrib.auth.models import User

class Activity():
    participants = models.ManyToManyField(User, related_name='activity_participants')

我想找到两个用户同时参与的所有活动。

我设法使用原始查询解决了我的问题(我的应用名称是“core”,因此表名中的“core”前缀):

SELECT ca.id FROM core_activity_participants AS cap, core_activity AS ca
INNER JOIN core_activity_participants AS cap2 ON cap.activity_id
WHERE cap.user_id == 1 AND cap2.user_id == 2
    AND cap.activity_id == cap2.activity_id
    AND ca.id == cap.activity_id

但是,如果可能的话,我想避免使用原始查询,因为它会破坏我应用其他部分的一致性。如何使用 Django 的 ORM 进行此查询或等效查询?

【问题讨论】:

    标签: sql django orm django-orm


    【解决方案1】:

    如果您使用的是 Django 1.11 或更高版本,intersection 查询集方法将为您提供所需的记录。

    # u1 and u2 are User instances
    u1_activities = Activity.objects.filter(participants=u1)
    u2_activities = Activity.objects.filter(participants=u2)
    common_activities = u1_activities.intersection(u2_activities)
    

    会产生类似这样的查询:

    SELECT "core_activity"."id"
    FROM "core_activity"
    INNER JOIN "core_activity_participants"
    ON ("core_activity"."id" = "core_activity_participants"."activity_id")
    WHERE "core_activity_participants"."user_id" = 1
    INTERSECT
    SELECT "core_activity"."id"
    FROM "core_activity"
    INNER JOIN "core_activity_participants"
    ON ("core_activity"."id" = "core_activity_participants"."activity_id")
    WHERE "core_activity_participants"."user_id" = 2
    

    如果您想检查超过 2 个用户之间的活动重叠,您还可以向交集添加额外的查询集。

    更新:

    另一种适用于旧 Django 版本的方法是

    u1_activities = u1.activity_participants.values_list('pk', flat=True)
    common_activities = u2.activity_participants.filter(pk__in=u1_activities)
    

    这会产生类似的查询

    SELECT "core_activity"."id"
    FROM "core_activity"
    INNER JOIN "core_activity_participants"
    ON ("core_activity"."id" = "core_activity_participants"."activity_id")
    WHERE (
        "core_activity_participants"."user_id" = 2
        AND "core_activity"."id" IN (
            SELECT U0."id"
            FROM "core_activity" U0
            INNER JOIN "core_activity_participants" U1
            ON (U0."id" = U1."activity_id")
            WHERE U1."user_id" = 1
        )
    )
    

    【讨论】:

    • 我使用的是 django 2.0。这正是我一直在寻找的。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2019-03-28
    • 1970-01-01
    • 2021-09-29
    • 1970-01-01
    • 1970-01-01
    • 2016-09-05
    • 2011-05-29
    • 1970-01-01
    相关资源
    最近更新 更多