目前,我有一个使用 django 和 rest_framework 来运行一些基本 API 的项目。
问题是,当我使用 rest_framework 和 DjangoModelPermissions 上的通用库创建视图时,我的方法 get_queryset 被调用两次
My权限类
class DefaultModelPermissions(DjangoModelPermissions):
"""
The request is authenticated using `django.contrib.auth` permissions.
See: https://docs.djangoproject.com/en/dev/topics/auth/#permissions
It ensures that the user is authenticated, and has the appropriate
`view`/`add`/`change`/`delete` permissions on the model.
This permission can only be applied against view classes that
provide a `.queryset` attribute.
"""
perms_map = {
'GET': ['%(app_label)s.view_%(model_name)s'],
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
'HEAD': ['%(app_label)s.view_%(model_name)s'],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
class DefaultModelViewPermissions:
permission_classes = (DefaultModelPermissions,)
我的视图
class AgentListCreateView(DefaultModelViewPermissions, generics.ListCreateAPIView):
serializer_class = serializers.AgentSerializer
def get_queryset(self):
print('get_queryset')
return models.Agent.objects.all()
调试应用程序,我发现权限还调用get_queryset来检索模型以验证django的用户权限。
permission_calling_get_queryset
从我的角度来看,这是不必要的操作。根据数据库表的大小,这可能是一个主要的性能问题,并导致许多其他应用程序问题。
我的解决方案是覆盖此“has_permission”方法。所以我的课就像
class DefaultModelPermissions(DjangoModelPermissions):
"""
The request is authenticated using `django.contrib.auth` permissions.
See: https://docs.djangoproject.com/en/dev/topics/auth/#permissions
It ensures that the user is authenticated, and has the appropriate
`view`/`add`/`change`/`delete` permissions on the model.
This permission can only be applied against view classes that
provide a `.queryset` attribute.
"""
perms_map = {
'GET': ['%(app_label)s.view_%(model_name)s'],
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
'HEAD': ['%(app_label)s.view_%(model_name)s'],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
def has_permission(self, request, view):
if not request.user or (
not request.user.is_authenticated and self.authenticated_users_only):
return False
# Workaround to ensure DjangoModelPermissions are not applied
# to the root view when using DefaultRouter.
if getattr(view, '_ignore_model_permissions', False):
return True
# Workaround cause base class utilized get_queryset to retrieve the model
# making unecessary requisitions to the database
if hasattr(view, 'get_serializer_class'):
model = view.get_serializer_class().Meta.model
else:
model = view.serializer_class.Meta.model
perms = self.get_required_permissions(request.method, model)
return request.user.has_perms(perms)
我的问题是: 有没有更好的方法来解决这个问题?还有其他人遇到同样的问题吗?
你的分析是对的,调用两次
get_queryset
确实不必要且低效。你的解决方案有效,但也有一些其他的解决方案可以考虑:
1. 使用缓存
你可以使用 Django 的缓存系统来缓存
get_queryset
的结果。这样,只有在缓存过期后才会实际执行查询。
from django.core.cache import cache
class AgentListCreateView(DefaultModelViewPermissions, generics.ListCreateAPIView):
serializer_class = serializers.AgentSerializer
def get_queryset(self):
print('get_queryset')
queryset = cache.get('agent_queryset')
if queryset is None:
queryset = models.Agent.objects.all()
cache.set('agent_queryset', queryset, 60 * 5) # 缓存 5 分钟
return queryset
2. 使用
has_object_permission
如果你的权限检查主要依赖于对象级别,你可以覆盖
has_object_permission
方法,而不是
has_permission
。这样,
get_queryset
只会被调用一次,然后
has_object_permission
会为每个对象调用一次。
class DefaultModelPermissions(DjangoModelPermissions):
# ...
def has_object_permission(self, request, view, obj):
# 在这里进行权限检查,使用 obj 而不是 view.queryset
3. 自定义权限类
你可以创建自己的权限类,完全控制何时以及如何调用
get_queryset
。
from rest_framework import permissions
class CustomModelPermissions(permissions.BasePermission):
def has_permission(self, request, view):
# 你的权限逻辑,只调用一次 get_queryset
if request.method in permissions.SAFE_METHODS:
queryset = view.get_queryset()
# ... 对 queryset 进行权限检查 ...
return True
# ... 其他方法的权限逻辑 ...
4. 使用 Django Guardian
如果你需要更复杂的权限管理,可以考虑使用 Django Guardian 等第三方库。它提供了基于对象的权限管理,可以更细粒度地控制权限。
总的来说,最佳解决方案取决于你的具体需求和应用程序的复杂性。 缓存通常是最简单的解决方案,而自定义权限类则提供了更大的灵活性。
标签:python,django,django-rest-framework,django-views,user-permissions From: 78809558