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)

Мой вопрос заключается в следующем: Есть ли лучший способ решить эту проблему? У кого-нибудь еще есть такая же проблема?

Ответ

  1. Нет необходимости создавать класс DefaultModelViewPermissions. Просто используйте встроенные настройки DRF
  2. .
# settings.py

'DEFAULT_PERMISSION_CLASSES': [
    ...
   'path.to.DefaultModelPermissions',
   ...
]
    <<<Метод
  1. DjangoModelPermissions.has_permission не выполняет запрос к БД. Он заставляет queryset использовать model_class из него без оценки.

*Django DOCS*

Внутри QuerySet можно строить, фильтровать, нарезать и в общем, передавать по кругу, не обращаясь к базе данных. Никакой активности в базе данных не происходит до тех пор, пока вы не сделаете что-то для оценки queryset.

Вы можете оценить QuerySet следующими способами:

  • Итерация
  • Асинхронная итерация
  • Нарезка
  • Пикинг/кэширование
  • repr()
  • len()
  • list()
  • bool()

Надеюсь, это поможет вам лучше понять querysets.

Вернуться на верх