【问题标题】:Django-Filters Empty String Query Param Cause Validation ErrorDjango-Filters 空字符串查询参数导致验证错误
【发布时间】:2019-08-04 14:56:36
【问题描述】:

当使用 django-filters 和 django rest 框架时,默认的查询过滤表单会为所有字段添加所有查询参数,空字段最终作为空字符串传递给后端。空字符串不是 None,因此 status = self.request.query_params.get('status', None) 仍会将空字符串添加到变量中,这将进入 queryset.filter 函数。当被过滤的字段不是字符串时,这非常糟糕。

所以我的问题是我做错了什么?有没有办法检查空字符串参数?我不确定我是否正确地进行过滤(我可能无缘无故地过滤了两次,但我想要 django-filters 在那里,因为它内置了与 django-rest-frameworks 可浏览 API 的集成。

我的解决方法是在调用 query_params.get 后使用三元运算符检查空字符串

以下是我对 API 的查看代码:

class JobList(generics.ListCreateAPIView):
    serializer_class = JobCreateSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

    filter_backends = (filters.OrderingFilter, DjangoFilterBackend)
    filterset_fields = ('status', 'success', 'worker_id', 'owner', 'job_type')
    ordering_fields = ('priority', 'submitted', 'assigned', 'completed')

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

    def get(self, request, format=None):
        # IMPORTANT: use self.get_queryset() any place you want filtering to be enabled
        # for built in filtering or ordering you must call self.filter_queryset
        jobs = self.filter_queryset(self.get_queryset())
        serializer = JobSerializer(jobs, context={'request': request}, many=True)

        return Response(serializer.data)

    def get_queryset(self):
        """
        This view should return a list of all the purchases
        for the currently authenticated user.
        """
        queryset = Job.objects.all()

        status = self.request.query_params.get('status', None)
        status = status if not status == '' else None
        success = self.request.query_params.get('success', None)
        success = success if not success == '' else None
        worker_id = self.request.query_params.get('worker_id', None)
        worker_id = worker_id if not worker_id == '' else None
        owner_id = self.request.query_params.get('owner', None)
        owner_id = owner_id if not owner_id == '' else None
        job_type = self.request.query_params.get('job_type', None)
        job_type = job_type if not job_type == '' else None

        if status is not None:
            queryset = queryset.filter(status=status)
        if success is not None:
            queryset = queryset.filter(success=success)
        if worker_id is not None:
            queryset = queryset.filter(worker_id=worker_id)
        if owner_id is not None:
            queryset = queryset.filter(owner=owner_id)
        if job_type is not None:
            queryset = queryset.filter(job_type=job_type)

        return queryset

【问题讨论】:

    标签: python django django-rest-framework django-filter


    【解决方案1】:

    感谢您的回答,我认为它们是有效的方法,但它们都在进行手动检查。我一直在寻找一种内置方法来将 query_params 附带的 JSON 本机类型转换为 Python 本机类型(false 到 False,null 到 None 等)。希望避免手动检查和转换每个query_param。

    如果这样的东西在 Django Rest 框架中不可用,那真的让我大吃一惊。所以我创建了一个辅助函数,它可以进行从 query_params 到 Python dict 的简单一级深度转换。我希望这对其他人有所帮助,或者如果有人知道 DRF 中有类似的便利功能,我将不胜感激。或者,如果有人能告诉我为什么这是一种不好的方法,我也将不胜感激!

    def query_params_parser(self, fields_list):
    
            json_values_dict = {}
    
            for field in fields_list:
                value = '"' + self.request.query_params.get(field) + '"' if not self.query_params.get(field, '') == '' else 'null'
                json_values_dict['"' + field + '"'] = value
    
            json_string = '{'
    
            for idx, (key, value) in enumerate(json_values_dict.items()):
                json_string += f'{key}: {value}'
    
                if idx < len(json_values_dict) - 1:
                    json_string += ','
    
            json_string += '}'
    
            final_dict = json.loads(json_string)
    
            return final_dict
    
    
    def get_queryset(self):
            """
            This view should return a list of all the purchases
            for the currently authenticated user.
            """
            queryset = Job.objects.all()
    
            # parse json query_params
            # query_params, list of fields
            # returns dict
    
            params_dict = self.query_params_parser(['status',
                                                    'success',
                                                    'worker_id',
                                                    'owner',
                                                    'job_type'])
    
            status = params_dict['status']
            success = params_dict['success']
            worker_id = params_dict['worker_id']
            owner = params_dict['owner']
            job_type = params_dict['job_type']
    
            if status is not None:
                queryset = queryset.filter(status=status)
            if success is not None:
                queryset = queryset.filter(success=success)
            if worker_id is not None:
                queryset = queryset.filter(worker_id=worker_id)
            if owner is not None:
                queryset = queryset.filter(owner=owner)
            if job_type is not None:
                queryset = queryset.filter(job_type=job_type)
    
            return queryset
    

    上述功能在有限的测试下工作,所以它可能有很多错误。

    【讨论】:

      【解决方案2】:

      如果您使用if status: 而不是if status is not None:,则空字符串和None 都会给出False。这对于整数很危险,因为 0 会返回 false,但这不是问题,因为所有参数都是字符串。

      您也可以将''定义为默认值,然后检查非零长度字符串:

      status = self.request.query_params.get('status', '')
      # ...
      if len(status):
      

      【讨论】:

        【解决方案3】:

        为什么不将默认值设置为空字符串,并将其用作保护值而不是 None

        status = self.request.query_params.get("status", "")
        success = self.request.query_params.get("success", "")
        # ...
        if status:
            queryset = queryset.filter(status=status)
        if success:
            queryset = queryset.filter(success=success)
        # ...
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-20
          • 2015-04-04
          • 2015-02-16
          • 1970-01-01
          相关资源
          最近更新 更多