I have the following models.
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()
I want to find all the documents that Person can access with the condition that they have to be a member of all of the allowed groups.
I want this: Case
If I do this:
person = Person.objects.get(pk=1)
Document.objects.filter(allowed_groups__in=person.permission_groups.all())
I would match on all of the above cases except 8 (not what I want)
There are many questions on stack overflow that are asking about exact matching i.e. match only on case 6 but not case 1. (also not what I want)
So my question is how can I use django to do this? I've considered using SQL but surely there is a way to do with Django ORM. It doesn't seem like that much of a crazy requirement.
Note: I have a few other conditions (other types of groups and Document access levels) which I'ved turned into a complicated expression with chained filter/Q Objects, but I've got all that worked out except for this bit.
Also: I'm having a bit of trouble phrasing my question title, which might be why I can't find an answer. It doesn't need to be query set, it could just be a list of pks or some other method.
Based on this answer
Solution:
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
)
And then its query will be something like this:
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