目前我有一个使用 django 和 rest_framework 的项目来获取一些运行中的基本 API。
问题是,当我使用rest_framework和DjangoModelPermissions上的通用库创建视图时,我的方法 get_queryset 被调用两次
我的许可类
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)
我的问题是: 有没有更好的方法来解决这个问题?还有人遇到同样的问题吗?
DefaultModelViewPermissions
类。只需使用内置的 DRF 设置# settings.py
'DEFAULT_PERMISSION_CLASSES': [
...
'path.to.DefaultModelPermissions',
...
]
DjangoModelPermissions.has_permission
方法不执行数据库查询。它正在获取查询集以使用其中的 model_class
而不进行评估。在内部,可以构造、过滤、切片和查询查询集 通常在没有实际访问数据库的情况下传递。不 数据库活动实际上会发生,直到您执行某些操作来评估 查询集。
您可以通过以下方式评估查询集:
- 迭代
- 异步迭代
- 切片
- 酸洗/缓存
- repr()
- len()
- 列表()
- 布尔()