New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve performance of shortcuts #796
Comments
Example for improvement in guardian shortcuts for direct model:
|
@ericmuijs |
Ended up writing a direct model only shortcut for another project. This may be useful for some people and maybe a PR can come out of it. Though I wasn't able to update the core library. Should result in a single query from functools import reduce
from operator import and_, or_
from typing import List, TypeVar, Union, cast
from django.contrib.auth.models import AbstractBaseUser, AnonymousUser
from django.db.models import Exists, Model, OuterRef, Q, QuerySet
from guardian.utils import get_group_obj_perms_model, get_user_obj_perms_model
T = TypeVar("T", bound=Model)
def get_objects_for_user(
user: Union[AbstractBaseUser, AnonymousUser],
perms: List[str],
klass: QuerySet[T],
any_perm: bool = False,
) -> QuerySet[T]:
"""
Fetches a queryset of objects for which the user has specified permissions.
Acts as a replacement for Django Guardian's `get_objects_for_user`, aiming
for flexible and efficient permission checks using Django's ORM.
Args:
user: User for whom to retrieve objects.
perms: Permission strings to check.
klass: Initial queryset of model objects.
any_perm: If True, returns objects for any permissions. Else, all.
Returns:
A queryset of objects with the specified permissions for the user.
Note:
- Dynamically builds queries for user/group permissions.
- Requires `klass` as a correct model type queryset and `perms` to be
model-appropriate permission codenames.
- Custom `UserObjectPermission` and `GroupObjectPermission` models
associate permissions with model instances, enabling granular access
control.
"""
if not user.is_authenticated or not perms:
return klass.none()
user_permissions_field = get_user_obj_perms_model(
klass.model
).permission.field.related_query_name()
group_permissions_field = get_group_obj_perms_model(
klass.model
).permission.field.related_query_name()
qs = klass
permission_filters = []
for perm in perms:
perm_codename = perm.split(".")[-1]
user_perm_query = Q(
**{
f"{user_permissions_field}__permission__codename": perm_codename,
f"{user_permissions_field}__user": user,
}
)
group_perm_query = Q(
**{
f"{group_permissions_field}__permission__codename": perm_codename,
f"{group_permissions_field}__group__user": user,
}
)
permission_filters.append(
Exists(klass.filter(user_perm_query | group_perm_query, pk=OuterRef("pk")))
)
if any_perm:
combined_condition = reduce(or_, permission_filters)
else:
combined_condition = reduce(and_, permission_filters)
return cast(
QuerySet[T],
qs.annotate(has_permission=combined_condition).filter(has_permission=True),
) |
Really like django guardian.
I noticed that the shortcuts use PK lists, which tend to be relatively slow for big groups. Can this be improved using a subquery?
Is this project still alive?
The text was updated successfully, but these errors were encountered: