Django-Filters Пустая строка Запрос Параметр Причина Ошибка проверки - PullRequest
2 голосов
/ 13 марта 2019

При использовании django-фильтров с каркасом отдыха django, форма фильтрации запросов по умолчанию добавит все параметры запроса для всех полей, и пустые поля в конечном итоге станут пустыми строками, переданными в бэкэнд.Пустые строки не являются None, поэтому status = self.request.query_params.get('status', None) все равно добавит пустую строку в переменную, и это попадет в функцию queryset.filter.Это очень плохо, когда фильтруемое поле не является строкой.

Итак, мой вопрос, я что-то не так делаю?Есть ли способ проверить наличие пустых строковых параметров?Я не уверен, правильно ли я делаю фильтрацию (возможно, я фильтрую дважды без причины, но мне нужны там фильтры django из-за встроенной интеграции с API просмотра django-rest-frameworks.

Мой обходной путь - троичный оператор, проверяющий пустую строку после вызова 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

Ответы [ 3 ]

0 голосов
/ 13 марта 2019

Если вы используете if status: вместо if status is not None:, обе пустые строки и None дают False.Это опасно для целых чисел, так как 0 вернет false, но здесь это не проблема, так как все параметры являются строками.

Вы также можете определить '' по умолчанию, а затем проверить ненулевое значение.длина строки:

status = self.request.query_params.get('status', '')
# ...
if len(status):
0 голосов
/ 14 марта 2019

Спасибо за ваши ответы, я думаю, что они правильные подходы, но они оба делают ручные проверки.Я искал встроенный способ преобразования нативных типов JSON, которые входят с query_params, в нативные типы Python (false в False, null в None и т. Д.).Надеясь избежать ручной проверки и преобразования каждого query_param.

Это действительно поражает, если такого нет в Django Rest Framework.Поэтому я создал вспомогательную функцию, которая может выполнять простое одноуровневое глубокое преобразование из 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

Вышеприведенная функция работает при ограниченном тестировании, поэтому, возможно, в ней много ошибок.

0 голосов
/ 13 марта 2019

Почему бы не установить по умолчанию пустую строку и использовать ее в качестве защитного значения вместо 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)
# ...
...