Suppose,
A model named Education
contains the fields degree
and field
, and other model Resume
contains the fields skill
and role
.
A third model is Candidates
and has a foreign key relation with the above models.
I want the user to search candidates by their skill
, role
, degree
or field
.
For example: if a query string like {'java','developer','MS','IT'}
is passed, Django should show the all the candidates matching any one of the values in the query string.
If you're doing this with the Django Rest Framework (DRF), you will want to use django_filters as referenced by DRF.
To do what you're talking about in my project, I created a generic extension of a 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
This is used in a django_filter.FilterSet
like this:
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',)
Which is instantiated like:
class SampleSetViewSet(viewsets.ModelViewSet):
queryset = SampleSet.objects.all()
serializer_class = SampleSetSerializer
filter_class = SampleSetFilterSet # <- and vvvvvvvvvvvvvvvvvvvvvvvvvvvv
filter_backends = (filters.OrderingFilter, filters.DjangoFilterBackend,)
Finally, a GET
request like:
http://www.example.com/rest/SampleSet/?multi_field_srch=foo,de,fa,fa
will return all SampleSet
's that have all of foo
, de
, and fa
in at least one of the fields field_foo
, bar
, or baz
.
If you specify parameter token_reducer
to be operator.or_
, then that query would return all SampleSet
s that have any of foo
, de
, or fa
in at least one of the fields field_foo
, bar
, or baz
.
Finally, token_prefix
and token_suffix
are a way to insert wild cards (substring matching) or other prefixes or suffixes.
I don't think there's an automatic way to do this in django. But you can always OR multiple searches together using Q. The basic usage of Q is as follows:
from django.db.models import Q
Education.objects.filter(
Q(degree__icontains=query) | Q(field__icontains=query)
To use multiple queries you can easily build together these statements using a for statement (assuming queries is a list or set of query strings):
q = Q()
for query in queries
q = q | Q(degree__icontains=query) | Q(field__icontains=query)
Education.objects.filter(q)
Now you'd want to search over multiple models, so you would have to include those joins as well. It's not exactly clear from your question how you'd want to search, but I'd guess you'd like to basically search for candidates and that all keywords need to be matched by the found items. So the query could be done like this:
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)
I'm using 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 which explains how to set up your endpoints. It seems really well built and documented, and to support everything I expect such as limits and filters.