是否可以进行模糊过滤搜索? (在一个字段上过滤一组值)
Is it possible to do fuzzy filter search? (filter on a field for a set of values)
我来自这个问题 https://github.com/carltongibson/django-filter/issues/919,这是发生的事情:
我想通过 django-filter 对列表做一些模糊搜索(某些列表搜索和单个模糊搜索都可以)。
一、产品型号:
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]
,是否可以这样做?
尝试 Django Fulltext Search 类似 TrigramSimilarity 搜索,但前提是您的数据库是 PostgreSQL。
对于其他数据库,可以使用外部 python 包 运行 查询集模糊搜索。
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')
我来自这个问题 https://github.com/carltongibson/django-filter/issues/919,这是发生的事情:
我想通过 django-filter 对列表做一些模糊搜索(某些列表搜索和单个模糊搜索都可以)。
一、产品型号:
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]
,是否可以这样做?
尝试 Django Fulltext Search 类似 TrigramSimilarity 搜索,但前提是您的数据库是 PostgreSQL。
对于其他数据库,可以使用外部 python 包 运行 查询集模糊搜索。
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')