首页 > 编程问答 >Django&rest_framework - 方法 get_queryset 被调用两次,还有其他更好的解决方案吗?

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

时间:2024-07-30 10:17:49浏览次数:16  
标签:python django django-rest-framework django-views user-permissions

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

相关文章

  • Open3D点云裁剪-用Python实现高效的点云处理技术
    点云数据是计算机视觉领域中常用的一种数据表示形式,它以三维空间中的离散点集合的方式来描述物体的形状和结构,在点云数据处理过程中,点云裁剪是一项关键任务,它可以帮助我们从原始的点云数据中提取出感兴趣的物体或区域,从而提高数据处理效率和准确性,本文将介绍如何使用Open3D库和......
  • Python,计算HSV图像的直方图,忽略背景
    我正在尝试使用openCV计算HSV图像的直方图,使用以下代码:defistogrammaHSV(image,histSize):hsv_planes=cv2.split(image)histSize=histSizehistRange=(0,256)accumulate=Falseh_hist=np.array(cv2.calcHist(hsv_planes,[0],None,[......
  • python性能分析器:cProfile
    代码:(1)importcProfileimportrecProfile.run('re.compile("foo|bar")')运行结果:(2)importcProfiledefrunRe():importrecProfile.runctx('re.compile("foo|bar")',None,locals())runRe()运行结果:(3)i......
  • 基于Python网络招聘数据可视化分析系统的设计与实现
    基于Python网络招聘数据可视化分析系统的设计与实现DesignandImplementationofPython-basedNetworkRecruitmentDataVisualizationAnalysisSystem完整下载链接:基于Python网络招聘数据可视化分析系统的设计与实现文章目录基于Python网络招聘数据可视化分析系......
  • 即使使用 docker run -dit 命令,python-Docker 容器也会在运行两秒后退出
    我想从此处使用Dockerfile测试自定义kubernetes调度程序:FROMpython:3.7RUNpipinstallkubernetesCOPYscheduler.py/scheduler.pyCMDpython/scheduler.py一旦创建了映像和容器:dockerbuild-tapp.dockercontainerrun-d-it--namemy-sched......
  • Python多重处理,如何避免创建具有百万个对象的元组
    python多处理新手。我有一项任务,涉及访问网络服务数百万次并将响应保存在文件中(每个请求都有单独的文件)。我已经得到了高级工作代码,但对一些事情没有感到困惑。以下两种语法有什么区别?pool=Pool(processes=4)pool.starmap(task,listOfInputParametersTu......
  • Python OpenCV - 显示坏像素检查测试
    我想找到显示器中存在的每个坏像素。坏像素可能是颜色不正确的像素,或者像素只是黑色。显示屏的尺寸为160x320像素。所以如果显示效果好的话,必须有160*320=51200像素。如果显示器没有51200像素,那就是坏的。另外,我想知道每个坏像素的位置。一旦拍摄的图像太大,我将共享一个......
  • 在python日志输出的每一行前面添加变量缩进
    我正在将日志记录构建到一个Python应用程序中,我希望它是人类可读的。目前,调试日志记录了调用的每个函数以及参数和返回值。这意味着,实际上,嵌套函数调用的调试日志可能如下所示:2024-07-2916:52:26,641:DEBUG:MainController.initialize_componentscalledwithargs<control......
  • 使用 DQN 实现 pong,使用 python 中的特征向量而不是像素。我的 DQNA 实现代码正确吗,因
    我正在致力于使用OpenAI的Gym为Pong游戏实现强化学习(RL)环境。目标是训练人工智能代理通过控制球拍来打乒乓球。代理收到太多负面奖励,即使它看起来移动正确。具体来说,奖励函数会惩罚远离球的智能体,但这种情况发生得太频繁,即使球朝球拍移动时似乎也会发生。观察......
  • Python CDLL 无法加载两次
    我正在尝试用python创建一个密码管理器,但遇到了一个问题,一旦加载了一种类型的dll,我就无法加载不同的dll,在这个示例中,我加载了一个dll,并尝试解密加密的密码数据,它工作正常,直到我加载另一个不同的nss3.dll文件,此时它给我一个错误:“过程入口点HeapAlloc无法位于动态链......