Можно ли сделать нечеткий фильтр поиска?(фильтр по полю для набора значений) - PullRequest
0 голосов
/ 28 мая 2018

Я пришел из этой проблемы https://github.com/carltongibson/django-filter/issues/919, вот что случилось:

Я хочу сделать нечеткий поиск по списку с помощью django-фильтра (определенный поиск по списку и одиночный нечеткий поиск в порядке).

Во-первых, модель продукта:

from django.db import models


class Product(models.Model):
    ...
    name = models.CharField(max_length=16)
    type = models.CharField(max_length=16)
    ...

и два продукта, хранящиеся в базе данных:

{
  "name": "tomato",
  "type": "vegetable"
}

{
  "name": "orange",
  "type": "fruit"
}

и сериализатор:

from rest_framework import serializers

from .models import Product


class ProductSerializer(serializers.ModelSerializer):
    name = serializers.CharField()
    type = serializers.CharFIeld()

    class Meta:
        model = Product
        fields = '__all__'

затем ListFilter :

import django_filters
from django_filters.fields import Lookup

from .models import Product


class ListFilter(django_filters.Filter):
    def filter(self, qs, value):
        value_list = value.split(u',')
        return super(ListFilter, self).filter(qs, Lookup(value_list, 'in'))

затем просмотр (я поставил ListFilter в utils.django_filters):

from rest_framework import generics
from django_filters import rest_framework as filters

from utils.django_filters import ListFilter

from .models import Product
from .serializers import ProductSerializer


class ProductFilter(filters.FilterSet):
    type = ListFilter(name='type')

    class Meta:
        model = Product
        fields = {
            'type': ['exact', 'contains'],
        }


class ProductList(generics.ListCreateAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_class = ProductFilter

наконец URL:

from django.urls import path

from . import views


path('product-list', views.ProductList.as_view(), name='product-list')

Ниже приведены контрольные примеры и результаты:

GET /api/product/product-list?type=fruit -> [orange]

GET /api/product/product-list?type=vegetable -> [tomato]

GET /api/product/product-list?type=fruit,vegetable -> [orange, tomato]

GET /api/product/product-list?type__contains=fru -> [orange]

GET /api/product/product-list?type__contains=vege -> [tomato]

GET /api/product/product-list?type__contains=fru,vege -> []

GET /api/product/product-list?type__contains=fruit,vegetable -> []

Я ожидаю, что последние два результата будут [orange, tomato], возможно ли это сделать?

Ответы [ 2 ]

0 голосов
/ 28 мая 2018

Наконец, это решено в соответствии с rpkilby 's answer :

from functools import reduce
from operator import or_

from django.db.models import Q
from django_filters import Filter, CharFilter
from django_filters.fields import Lookup
from django_filters.filters import BaseCSVFilter


SPLIT_SIGN = u','


class ListFilter(Filter):
    def filter(self, qs, value):
        value_list = value.split(SPLIT_SIGN)
        return super(ListFilter, self).filter(qs, Lookup(value_list, 'in'))


def gen_fuzzy_list_filter(attr_name):
    class CharListFilter(BaseCSVFilter, CharFilter):
        @staticmethod
        def filter(qs, value):
            # BaseCSVFilter produces/validates a list of values
            value_list = SPLIT_SIGN.join(value).split(SPLIT_SIGN)
            queries = [Q(**{attr_name: val}) for val in value_list]
            return qs.filter(reduce(or_, queries))

    return CharListFilter

затем в фильтре:

class ProductFilter(filterset.FilterSet):
    type = ListFilter(name='type')
    type__contains = gen_fuzzy_list_filter('type__contains')(field_name='type', lookup_expr='contains')
0 голосов
/ 28 мая 2018

Попробуйте Django Полнотекстовый поиск , такой как поиск TrigramShoity, но только если ваша БД - PostgreSQL.

Для других БД может использоваться внешний пакет python, выполняющий нечеткий поиск по наборам запросов.

...