|
1 |
| -from typing import TYPE_CHECKING |
| 1 | +from django.contrib.auth.models import BaseUserManager, Group, Permission |
| 2 | +from django.contrib.postgres.aggregates import ArrayAgg |
| 3 | +from django.db.models import Prefetch, Q, QuerySet |
2 | 4 |
|
3 |
| -from django.contrib.auth.models import BaseUserManager, Permission |
4 |
| -from django.db.models import Q, QuerySet |
5 | 5 |
|
6 |
| -if TYPE_CHECKING: |
7 |
| - from .models import User |
| 6 | +class UserQuerySet(QuerySet): |
| 7 | + def annotate_permissions(self) -> "UserQuerySet": |
| 8 | + """ |
| 9 | + Adds `user_permission_codenames` and `group_permission_codenames` as `ArrayField` to the current QuerySet |
| 10 | + containing the codenames of the applicable permissions. `user_permissions` and `permissions` are prefetched to |
| 11 | + minimize queries. |
| 12 | +
|
| 13 | + This is used to avoid n+1 issues with nested `UserSerializer`. |
| 14 | + """ |
| 15 | + return self.prefetch_related( |
| 16 | + Prefetch( |
| 17 | + "user_permissions", |
| 18 | + queryset=Permission.objects.select_related("content_type").order_by( |
| 19 | + "codename" |
| 20 | + ), |
| 21 | + ), |
| 22 | + Prefetch( |
| 23 | + "groups", |
| 24 | + queryset=Group.objects.prefetch_related( |
| 25 | + Prefetch( |
| 26 | + "permissions", |
| 27 | + queryset=Permission.objects.select_related( |
| 28 | + "content_type" |
| 29 | + ).order_by("codename"), |
| 30 | + ) |
| 31 | + ), |
| 32 | + ), |
| 33 | + ).annotate( |
| 34 | + user_permission_codenames=ArrayAgg( |
| 35 | + "user_permissions__codename", distinct=True |
| 36 | + ), |
| 37 | + group_permission_codenames=ArrayAgg( |
| 38 | + "groups__permissions__codename", distinct=True |
| 39 | + ), |
| 40 | + ) |
8 | 41 |
|
9 | 42 |
|
10 |
| -class UserManager(BaseUserManager): |
| 43 | +class UserManager(BaseUserManager.from_queryset(UserQuerySet)): |
11 | 44 | use_in_migrations = True
|
12 | 45 |
|
13 | 46 | def _create_user(self, username, email, password, **extra_fields):
|
@@ -39,19 +72,19 @@ def create_superuser(self, username, email, password, **extra_fields):
|
39 | 72 |
|
40 | 73 | return self._create_user(username, email, password, **extra_fields)
|
41 | 74 |
|
42 |
| - def _users_with_permission(self, permission: Permission) -> QuerySet["User"]: |
| 75 | + def _users_with_permission(self, permission: Permission) -> UserQuerySet: |
43 | 76 | return self.filter(
|
44 | 77 | Q(groups__permissions=permission) | Q(user_permissions=permission)
|
45 | 78 | ).distinct()
|
46 | 79 |
|
47 |
| - def main_reviewers(self) -> QuerySet["User"]: |
| 80 | + def main_reviewers(self) -> UserQuerySet: |
48 | 81 | permission = Permission.objects.get(codename="can_review_destruction")
|
49 | 82 | return self._users_with_permission(permission)
|
50 | 83 |
|
51 |
| - def archivists(self) -> QuerySet["User"]: |
| 84 | + def archivists(self) -> UserQuerySet: |
52 | 85 | permission = Permission.objects.get(codename="can_review_final_list")
|
53 | 86 | return self._users_with_permission(permission)
|
54 | 87 |
|
55 |
| - def co_reviewers(self) -> QuerySet["User"]: |
| 88 | + def co_reviewers(self) -> UserQuerySet: |
56 | 89 | permission = Permission.objects.get(codename="can_co_review_destruction")
|
57 | 90 | return self._users_with_permission(permission)
|
0 commit comments