Django过滤器,其中对象的多对多列表中的每一项都包含在提供的查询集中

xcitsw88  于 2022-12-24  发布在  Go
关注(0)|答案(2)|浏览(83)

我有以下型号。

class Document(models.Model):
  allowed_groups = models.ManyToManyField(Group, related_name='allowed_documents')

class Person(models.Model):
  permission_groups = models.ManyToManyField(Group, related_name='people')

class Group(models.Model):
  id = models.BigIntegerField()

我想查找Person可以访问的所有文档,条件是他们必须是所有允许组的成员。
我想要这个:案件
1.具有人员(权限组= 1、2、6、7、11、15)的文档(允许组= 1、2、7)-〉匹配
1.具有人员(权限组= 1,7)的文档(允许组= 1,2,7)-〉NO_MATCH
1.具有人员(权限组= 1,2)的文档(允许组= 1,2,7)-〉NO_MATCH
1.具有人员(权限组=2)的文档(允许组= 1、2、7)-〉NO_MATCH
1.具有人员(权限组=8)的文档(允许组= 1、2、7)-〉NO_MATCH
1.具有人员(权限组= 1,2,7)的文档(允许组= 1,2,7)-〉匹配
如果我这样做:

person = Person.objects.get(pk=1)
Document.objects.filter(allowed_groups__in=person.permission_groups.all())

我会匹配所有上述情况下,除了8(不是我想要的)
有很多关于堆栈溢出的问题都是关于精确匹配的,即只匹配第6种情况,而不匹配第1种情况。(也不是我想要的)
所以我的问题是如何使用Django来实现这一点?我曾考虑过使用SQL,但肯定有一种方法可以使用Django ORM。这似乎不是一个疯狂的要求。
注意:我还有一些其他的条件(其他类型的组和文档访问级别),我已经将它们转换成了一个带有链式filter/Q对象的复杂表达式,但是除了这一点之外,我已经解决了所有的问题。
还有:我在表达问题标题时遇到了一些麻烦,这可能就是为什么我找不到答案的原因。它不需要是查询集,它可以只是一个pk列表或其他方法。

zbdgwd5y

zbdgwd5y1#

使用numpy库中的np.isin函数。比较两个数组时返回布尔数组。说明here.
我将values_list和flat=True应用到对象中,将它们提取到列表中,以便在numpy中进行比较。我创建了一个列表生成器aaa(列表生成器比循环快很多倍),其中文档与选定的人进行比较,如果所有文档值都匹配,则all()返回True,然后将i.pk写入aaa列表。接下来,文档数据由该列表过滤并传递到字典以显示在模板中。

**将bboard替换为放置模板的文件夹名称。**我有这个:模板/bboard,它们位于应用程序文件夹中。

views.py

import numpy as np

def info(request):
    person = Person.objects.get(pk=1).permission_groups.all().values_list('id', flat=True)
    print(person)

    def my_func(x):
        document = Document.objects.get(pk=x).allowed_groups.all().values_list('id', flat=True)

        return document

    aaa = [i.pk for i in Document.objects.all() if np.isin(my_func(i.pk), person).all()]
    document = Document.objects.filter(pk__in=aaa)

    return render(request, 'bboard/templ.html', {'context': document})

模板

{% for a in context %}
<p>{{ a.id }}</p>
{% endfor %}

2022年10月26日更新

import numpy as np

def info(request):
    person = Person.objects.get(pk=1).permission_groups.all().values_list('id', flat=True)

    def my_func(i):
        document = i.allowed_groups.all().values_list('id', flat=True)

        return document

    aaa = [i.pk for i in Document.objects.prefetch_related('allowed_groups').all() if np.isin(my_func(i), person).all()]
    document = Document.objects.filter(pk__in=aaa)

    return render(request, 'bboard/templ.html', {'context': document})
mhd8tkvw

mhd8tkvw2#

基于this answer
溶液:

from django.db.models import Count, Q

person = Person.objects.get(pk=1)
permission_groups = set(person.permission_groups.all())
Document.objects.annotate(
    allowed_groups_count=Count('allowed_groups', filter=Q(allowed_groups__in=permission_groups))
).filter(
    allowed_groups_count__gt=0
)

然后它的查询将是这样的:

SELECT
  document.id,
  COUNT(document_allowed_groups.group_id) FILTER (
    WHERE
      document_allowed_groups.group_id IN (1, 2, 6, 7, 11, 15)
  ) AS allowed_groups_count
FROM
  document
  LEFT OUTER JOIN document_allowed_groups ON (
    document.id = document_allowed_groups.document_id
  )
GROUP BY
  document.id
HAVING
  COUNT(document_allowed_groups.group_id) FILTER (
    WHERE
      (
        document_allowed_groups.group_id IN (1, 2, 6, 7, 11, 15)
      )
  ) > 0

相关问题