在 Django 的多个模型中搜索字段
Search fields in multiple models in Django
假设,
名为 Education
的模型包含字段 degree
和 field
,另一个模型 Resume
包含字段 skill
和 role
.
第三个模型是Candidates
,与上述模型有外键关系。
我希望用户通过 skill
、role
、degree
或 field
来搜索候选人。
例如:如果传递了一个像 {'java','developer','MS','IT'}
这样的查询字符串,Django 应该显示匹配查询字符串中任何一个值的所有候选。
我认为在 django 中没有自动执行此操作的方法。但您始终可以使用 Q 将多个搜索组合在一起。Q 的基本用法如下:
from django.db.models import Q
Education.objects.filter(
Q(degree__icontains=query) | Q(field__icontains=query)
要使用多个查询,您可以使用 for 语句轻松地将这些语句构建在一起(假设查询是一个列表或一组查询字符串):
q = Q()
for query in queries
q = q | Q(degree__icontains=query) | Q(field__icontains=query)
Education.objects.filter(q)
现在您想要搜索多个模型,因此您还必须包括这些连接。从你的问题中并不清楚你想如何搜索,但我猜你想基本上搜索候选人并且所有关键字都需要与找到的项目匹配。所以查询可以这样完成:
q = Q()
for query in queries
q = (q & (Q(education__degree__icontains=query) |
Q(education__field__icontains=query) |
Q(resume__skill__icontains=query) |
Q(resume__role__icontains=query)
Q(skill__icontains=query) |
Q(role__icontains=query) |
Q(degree__icontains=query) |
Q(field__icontains=query)))
return Candidate.objects.filter(q)
如果您使用 Django Rest Framework (DRF) 执行此操作,则需要使用 django_filters as referenced by DRF.
为了完成您在我的项目中所说的事情,我创建了 django_filters.Filter
:
的通用扩展
import operator
from django.db.models import Q
import django_filters
class MultiFieldFilter(django_filters.Filter):
def __init__(self,names,*args,**kwargs):
if len(args) == 0:
kwargs['name'] = names[0]
self.token_prefix = kwargs.pop('token_prefix','')
self.token_suffix = kwargs.pop('token_suffix','')
self.token_reducer = kwargs.pop('token_reducer',operator.and_)
self.names = names
django_filters.Filter.__init__(self,*args,**kwargs)
def filter(self,qs,value):
if value not in (None,''):
tokens = value.split(',')
return qs.filter(
reduce(
self.token_reducer,
[
reduce(
operator.or_,
[Q(**{
'%s__icontains'%name:
(self.token_prefix+token+self.token_suffix)})
for name in self.names])
for token in tokens]))
return qs
这在 django_filter.FilterSet
中使用,如下所示:
class SampleSetFilter(django_filters.FilterSet):
multi_field_search = MultiFieldFilter(names=["field_foo", "bar", "baz"],lookup_type='in')
class Meta:
model = SampleSet
fields = ('multi_field_srch',)
实例化如下:
class SampleSetViewSet(viewsets.ModelViewSet):
queryset = SampleSet.objects.all()
serializer_class = SampleSetSerializer
filter_class = SampleSetFilterSet # <- and vvvvvvvvvvvvvvvvvvvvvvvvvvvv
filter_backends = (filters.OrderingFilter, filters.DjangoFilterBackend,)
最后,GET
请求如下:
http://www.example.com/rest/SampleSet/?multi_field_srch=foo,de,fa,fa
将 return 所有 SampleSet
具有 所有 的 foo
、de
、和 fa
至少在字段 field_foo
、bar
或 baz
.
之一
如果您将参数 token_reducer
指定为 operator.or_
,则该查询将 return 具有 any[=56= 的所有 SampleSet
foo
、de
、 或 fa
的至少一个字段 field_foo
、bar
或baz
.
最后,token_prefix
和token_suffix
是一种插入通配符(子串匹配)或其他前缀或后缀的方法。
我正在使用 Django Rest Multiple Models to search on multiple models in Django Rest Framework. Make sure to read the docs carefully, especially the section on using viewsets,它解释了如何设置您的端点。它似乎构建和记录得很好,并且支持我期望的一切,例如限制和过滤器。
假设,
名为 Education
的模型包含字段 degree
和 field
,另一个模型 Resume
包含字段 skill
和 role
.
第三个模型是Candidates
,与上述模型有外键关系。
我希望用户通过 skill
、role
、degree
或 field
来搜索候选人。
例如:如果传递了一个像 {'java','developer','MS','IT'}
这样的查询字符串,Django 应该显示匹配查询字符串中任何一个值的所有候选。
我认为在 django 中没有自动执行此操作的方法。但您始终可以使用 Q 将多个搜索组合在一起。Q 的基本用法如下:
from django.db.models import Q
Education.objects.filter(
Q(degree__icontains=query) | Q(field__icontains=query)
要使用多个查询,您可以使用 for 语句轻松地将这些语句构建在一起(假设查询是一个列表或一组查询字符串):
q = Q()
for query in queries
q = q | Q(degree__icontains=query) | Q(field__icontains=query)
Education.objects.filter(q)
现在您想要搜索多个模型,因此您还必须包括这些连接。从你的问题中并不清楚你想如何搜索,但我猜你想基本上搜索候选人并且所有关键字都需要与找到的项目匹配。所以查询可以这样完成:
q = Q()
for query in queries
q = (q & (Q(education__degree__icontains=query) |
Q(education__field__icontains=query) |
Q(resume__skill__icontains=query) |
Q(resume__role__icontains=query)
Q(skill__icontains=query) |
Q(role__icontains=query) |
Q(degree__icontains=query) |
Q(field__icontains=query)))
return Candidate.objects.filter(q)
如果您使用 Django Rest Framework (DRF) 执行此操作,则需要使用 django_filters as referenced by DRF.
为了完成您在我的项目中所说的事情,我创建了 django_filters.Filter
:
import operator
from django.db.models import Q
import django_filters
class MultiFieldFilter(django_filters.Filter):
def __init__(self,names,*args,**kwargs):
if len(args) == 0:
kwargs['name'] = names[0]
self.token_prefix = kwargs.pop('token_prefix','')
self.token_suffix = kwargs.pop('token_suffix','')
self.token_reducer = kwargs.pop('token_reducer',operator.and_)
self.names = names
django_filters.Filter.__init__(self,*args,**kwargs)
def filter(self,qs,value):
if value not in (None,''):
tokens = value.split(',')
return qs.filter(
reduce(
self.token_reducer,
[
reduce(
operator.or_,
[Q(**{
'%s__icontains'%name:
(self.token_prefix+token+self.token_suffix)})
for name in self.names])
for token in tokens]))
return qs
这在 django_filter.FilterSet
中使用,如下所示:
class SampleSetFilter(django_filters.FilterSet):
multi_field_search = MultiFieldFilter(names=["field_foo", "bar", "baz"],lookup_type='in')
class Meta:
model = SampleSet
fields = ('multi_field_srch',)
实例化如下:
class SampleSetViewSet(viewsets.ModelViewSet):
queryset = SampleSet.objects.all()
serializer_class = SampleSetSerializer
filter_class = SampleSetFilterSet # <- and vvvvvvvvvvvvvvvvvvvvvvvvvvvv
filter_backends = (filters.OrderingFilter, filters.DjangoFilterBackend,)
最后,GET
请求如下:
http://www.example.com/rest/SampleSet/?multi_field_srch=foo,de,fa,fa
将 return 所有 SampleSet
具有 所有 的 foo
、de
、和 fa
至少在字段 field_foo
、bar
或 baz
.
如果您将参数 token_reducer
指定为 operator.or_
,则该查询将 return 具有 any[=56= 的所有 SampleSet
foo
、de
、 或 fa
的至少一个字段 field_foo
、bar
或baz
.
最后,token_prefix
和token_suffix
是一种插入通配符(子串匹配)或其他前缀或后缀的方法。
我正在使用 Django Rest Multiple Models to search on multiple models in Django Rest Framework. Make sure to read the docs carefully, especially the section on using viewsets,它解释了如何设置您的端点。它似乎构建和记录得很好,并且支持我期望的一切,例如限制和过滤器。