Почему DjangoFilterBackend не возвращает результатов, если значение запроса не существует в моих данных? - PullRequest
0 голосов
/ 29 ноября 2018

Я использую фильтры drf и django, чтобы выполнить некоторую фильтрацию моих данных.Для фильтрующего бэкэнда я использую django_filters.rest_framework.DjangoFilterBackend.

class ProductsViewSet(LoginRequiredMixin, ModelViewSet):
    authentication_classes = (authentication.SessionAuthentication,)
    permission_classes = (permissions.IsAuthenticated,)

    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    filter_backends = (django_filters.rest_framework.DjangoFilterBackend,
                       rest_framework.filters.SearchFilter,
                       rest_framework.filters.OrderingFilter,)
    filter_class = ProductFilter

    search_fields = ("title", "description", "company", "country", "status",)
    ordering_fields = ("title", "description", "company", "country", "status",)

Мой класс_фильтра:

class ProductFilter(filters.FilterSet):

    price_from = filters.NumberFilter(field_name="price", lookup_expr="gte",
                                             label="Min price",
                                             min_value=0)
    price_to = filters.NumberFilter(field_name="price", lookup_expr="lte",
                                          label="Max price",
                                           min_value=0)

    # MultipleChoiceFilters
    country = filters.MultipleChoiceFilter(field_name="country", choices=COUNTRY_CHOICES)
    company = filters.MultipleChoiceFilter(field_name="company", choices=COMPANY_CHOICES)
    status = filters.MultipleChoiceFilter(field_name="status", choices=STATUS_CHOICES)

    class Meta:
        model = Product
        fields = ["price_from", "price_to", "country",
                  "company", "status"]
    @property
    def qs(self):
        parent = super(ProductFilter, self).qs
        return parent.order_by("-timestamp")

    def __init__(self, *args, **kwargs):
        super(ProductFilter, self).__init__(*args, **kwargs)
        user = self.request.user
        user_products = Product.objects.filter(user=user)

        # Initial Data #############################################################

        price_min = user_products.all().aggregate(Min("price"))["price__min"]
        price_max = user_products.all().aggregate(Max("price"))["price__max"]

        self.filters["price_from"].extra["initial"] = price_min
        self.filters["price_to"].extra["initial"] = price_max

        COUNTRY_CHOICES = tuple(Product.objects.filter(user=user).values_list("country", "country__name").distinct().order_by("country__name"))
        self.filters["country"].extra['choices'] = COUNTRY_CHOICES

        COMPANY_CHOICES = tuple(Product.objects.filter(user=user).values_list("company", "company").distinct().order_by("company"))
        self.filters["company"].extra['choices'] = COMPANY_CHOICES

Моя проблема заключается в том, что если я пытаюсь фильтровать по нескольким значениям, а одно из них не существуетЯ не получаю никаких результатов.

Например, если я хочу фильтровать по полевой компании, которая является ForeignKey, используя 2 значения:

  • компания Google с идентификатором= 2 не существует ни в одном из моих объектов, и

  • компания Microsoft с идентификатором = 3, который существует,

вернет следующеенет результатов

api/products?company=2&company=3

То же самое происходит, если поле просто CharField.Кроме того, такое же поведение возникает при использовании filter_fields и filter_class.

Сначала я реализовал свою собственную настройку фильтрации get_queryset, но подумал, что я мог бы сделать вещи менее сложными, используя бэкэнд фильтра.

Я не уверен, почему это происходит, если существуют два значения, с которыми я фильтрую, все работает нормально.

Ответы [ 2 ]

0 голосов
/ 29 ноября 2018

В вашем классе ProductFilter вы можете указать метод, который поле использует для выполнения запроса.С помощью фильтров вам нужно будет объединить эти значения для поля company, чтобы выполнить поиск таким образом.То, что вы делаете, - это предоставление отдельных ценностей.То, что вы хотите сделать, это изменить его на что-то вроде:

api/products?company=2+3

class ProductFilter(django_filters.FilterSet):

    company = django_filters.CharFilter(
        label=_('Company'),
        method='company_filter'
    )

    def company_box_filter(self, qs, name, value):
        """
        Override the filter method in order to lookup for more than one field.
        """
        if not value:
            return qs

        ids = value.split()

        return qs.filter(id__in=ids)
0 голосов
/ 29 ноября 2018

Моя проблема в том, что если Я пытаюсь отфильтровать по нескольким значениям, а одно из них не существует, я не получаю результатов .


Этоиз-за фильтрации происходит ЛОГИЧЕСКОЕ И мода.Это означает, что создаст запрос ORM , похожий на

Product.objects<b>.filter(company=1).filter(company=2)</b>

В SQL это что-то вроде:

SELECT ... from table <b>WHERE company=1 and company=2</b>

Одним из простых решений являетсядобавьте аргумент lookup_expr в свой класс фильтра как,

class ProductFilter(filters.FilterSet):
    # your code
    company = filters.MultipleChoiceFilter(
        field_name="company", choices=COMPANY_CHOICES,
        <b>lookup_expr='in'</b>)

Если вы используете аргумент filter_classпо вашему мнению, он не позаботится о filter_fields аргументах

...