Django&rest_framework - 方法get_queryset被调用两次,还有其他更好的解决方案吗?

问题描述 投票:0回答:1

目前我有一个使用 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)

我的问题是: 有没有更好的方法来解决这个问题?还有人遇到同样的问题吗?

python django django-rest-framework django-views user-permissions
1个回答
0
投票

回答

  1. 无需创建
    DefaultModelViewPermissions
    类。只需使用内置的 DRF 设置
# settings.py

'DEFAULT_PERMISSION_CLASSES': [
    ...
   'path.to.DefaultModelPermissions',
   ...
]
  1. DjangoModelPermissions.has_permission
    方法不执行数据库查询。它正在获取查询集以使用其中的
    model_class
    而不进行评估。

*Django 文档*

在内部,可以构造、过滤、切片和查询查询集 通常在没有实际访问数据库的情况下传递。不 数据库活动实际上会发生,直到您执行某些操作来评估 查询集。

您可以通过以下方式评估查询集:

  • 迭代
  • 异步迭代
  • 切片
  • 酸洗/缓存
  • repr()
  • len()
  • 列表()
  • 布尔()
© www.soinside.com 2019 - 2024. All rights reserved.