I’ve recently been given a new requirement which involves filtering objects based on some custom user permissions. The requirement is quite straightforward but I’ve come unstuck at the following:
A user interacts with cases and every case has a group
Group
class Group(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
name = models.CharField(max_length=128, unique=True)
Case
class Case(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
title = models.CharField(max_length=128)
group = models.ForeignKey(Group, related_name="cases", on_delete=models.CASCADE)
Based on some permissions, I filter the cases a user can see. This part is working as expected.
However, I have an endpoint which must list all groups in which there are cases a user can access along with the count of the total number of cases in each group to which a user can access.
As an example, let’s look at three users:
- Julie has access to four cases in the group birds
- Jenny has access to two cases in the group birds
- Juliet has access to two cases in the group birds and three cases in the group insects
When presenting a summary of available groups, the user should see every group for which they have access to cases within that group, and the count of the cases to which they have access in that group. They should not see groups in which there are no cases to which they have access.
As an example of the desired result for each of the above users.
Julie : has four cases in birds, no cases in any other group
[
{
"name": "birds",
"case_count": 4
}
]
Jenny: has two cases in birds, no cases in any other group
[
{
"name": "birds",
"case_count": 2
}
]
Juliet: has two cases in birds and three cases in insects
[
{
"name": "birds",
"case_count": 2,
},
{
"name": "insects",
"case_count": 3,
}
]
Until this permissions requirement was presented to me, I used to get all of the groups which had a case count greater than 0.
return Group.objects.annotate(
case_count=Count("cases")
).filter(case_count__gt=0)
Now that I have to deal with the permissions, I first filter the user’s case queryset, and for Juliet it would look something like this:
Queryset = [
<birds case 1>,
<birds case 2>,
<insects case 1>,
<insects case 2>,
<insects case 3>,
]
Note: It is a Django Queryset that I am returning and what I am representing above. It is not an array.
Using the data from the above queryset, I want to get the group of each case, return only a single instance for a group along with the case count from the case queryset, a la the depicted JSON data for Julie, Jenny and Juliet.
What I have now is the following:
def get_queryset(self):
# cases is Queryset containing all of the cases in
# all of the groups that a user has permission to access.
cases = case_query_filter(self.request.user)
# cases is the related name from Case.group to Group
case_filter = Q(cases__in=cases)
return (
Group.objects.filter(case_filter)
.annotate(case_count=Count("cases", filter=case_filter))
.filter(case_count__gt=0)
)
This queryset correctly returns only the groups with cases that a user can access. That is, Julie and Jenny does not see an insects group, but Juliet does.
The issue that I face is that the case count that is returned is the count for Case.objects.all(group=group).count() as opposed to the count of cases in each group to which the user has access.
Result from Group endpoint
[
{
"name": "birds",
"case_count": 8, # this should be 2
},
{
"name": "insects",
"case_count": 20, # this should be 3
}
]
Apologies if this was a bit long winded, but I felt that it would help with a quite verbose explaination.
Super hero status to anyone who can point my in the right direction.
Cheers,
C