【问题标题】:Check the existence of instance with Django ManyToManyField使用 Django ManyToManyField 检查实例是否存在
【发布时间】:2019-01-17 05:57:53
【问题描述】:

大家好!

如何使用ManyToManyField()检查数据库中是否已有类似记录:

class Room():
     created_by = models.ForeignKey(User)
     allowed_users = models.ManyToMany(User,related_name='rooms_as_guest')
     is_active = models.BooleanField(default=True)

在我看来,在创建新房间之前,我想确保没有一个活动房间有完全相同的参与者。

下面的例子是行不通的,它只是向你展示我的期望

 allowed_users = User.objects.filter(id__in=request.POST.get("ids",[]))
 if allowed_users:
     Room.objects.get_or_create(
         created_by = request.user,
         allowed_users = allowed_users, # won't work
         is_active = True,
     )

我怎样才能继续做我想做的事?

【问题讨论】:

    标签: django django-queryset django-orm manytomanyfield


    【解决方案1】:

    这是因为allowed_users 是一个ManyToMany 字段,所以在向其添加用户之前需要创建房间。

    在此处查看官方文档: https://docs.djangoproject.com/en/2.1/topics/db/examples/many_to_many/#many-to-many-relationships

    现在另一个问题是我们需要获得具有相同用户集的现有房间,这已经在此处的类似问题中得到了回答:

    Django filter where ManyToMany field contains ALL of list

    试试下面:

    from django.db.models import Count
    
    allowed_users = User.objects.filter(id__in=request.POST.get("ids",[]))
    if allowed_users:
        room = Room.objects.filter(allowed_users__in=allowed_users).annotate(num_users=Count('allowed_users')).filter(num_users=len(allowed_users)).first()
        if not room:
            room = Room.objects.create(created_by=request.user, is_active=True)
            room.allowed_users.add(*allowed_users)
    

    【讨论】:

    • 我想检查是否没有活动房间的参与者完全相同,如果是,请获取房间而不是创建新房间
    • @EuChi 知道了,正在修改。
    • 这看起来很完美,你能解释一下你有annotateCount的部分吗......annotate(num_users=Count('allowed_users')).filter(num_users=len(allowed_users))
    • annotate 用于group_by 操作,然后只获取num_users 等带注释的值。 Count 用于从 group_by 操作中获取每个 group 的计数。然后.filter(num_users=len(allowed_users) 将在查询中添加一个having 子句。
    • 您可以通过在查询集上附加.query 来获得确切的SQL 查询让allowed_users = [1,2,3,4,5] 然后len(allowed_users) 将是5 查询将看起来像: SELECT "rooms"."id", COUNT("room_users"."user_id") AS "num_users" FROM "rooms" LEFT OUTER JOIN "room_users" ON ("room"."id" = "room_users". "room_id") WHERE ("room_users"."user_id" IN (1,2,3,4,5)) GROUP BY "room"."id" HAVING COUNT("room_users"."user_id") = 5跨度>
    【解决方案2】:

    您不能使用 ManyToMany 字段创建这样的对象,因为该对象必须存在才能创建代表您的 m2m 关系的中间对象。您必须执行以下操作:

    allowed_users = User.objects.filter(id__in=request.POST.get("ids", [])).order_by('id')
    
    room = None
    for r in up.rooms.filter(is_active=True):
        if list(allowed_users) == list(r.allowed_users.all().order_by('id')):
            room = r # get the existing room
            break
    
    if not room:
        room = Room.objects.create(
            created_by = request.user,
            is_active = True
        )
        room.allowed_users.add(*allowed_users)
    

    但是,您必须确保 allowed_users 与 room.allowed_users 的顺序相同。

    Check the docs 了解更多信息

    【讨论】:

    • 如果 get_or_create 完成,可能会将 allowed_users 添加到现有房间。如果不存在具有 allowed_users 的房间,则应创建一个新房间。
    • 我想检查是否没有活动房间的参与者完全相同,如果是,请获取房间而不是创建新房间
    • allowed_users 是一个查询集,而不是一个列表......比较不起作用。但我明白你的意思
    • @EuChi 对,下单也已修复。
    • 我认为这会起作用,但是没有比 for 循环更好的方法了吗??
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-11
    • 2021-08-20
    • 2019-09-01
    • 2015-12-27
    • 2023-03-22
    • 2017-10-09
    • 1970-01-01
    相关资源
    最近更新 更多