Django & rest_framework - метод get_queryset вызывается дважды, есть ли другое лучшее решение?
В настоящее время у меня есть проект, использующий django и rest_framework для получения некоторых базовых API на ходу.
Проблема в том, что когда я делаю представление, используя generic lib на 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
С моей точки зрения, это ненужная операция. В зависимости от размера таблицы DB, это может стать серьезной проблемой производительности и привести ко многим другим проблемам приложения.
Моим решением было переопределить этот метод "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
не выполняет запрос к БД. Он заставляет queryset использоватьmodel_class
из него без оценки.
*Django DOCS*
Внутри QuerySet можно строить, фильтровать, нарезать и в общем, передавать по кругу, не обращаясь к базе данных. Никакой активности в базе данных не происходит до тех пор, пока вы не сделаете что-то для оценки queryset.
Вы можете оценить QuerySet следующими способами:
- Итерация
- Асинхронная итерация
- Нарезка
- Пикинг/кэширование
- repr()
- len()
- list()
- bool()