Skip to content

Instantly share code, notes, and snippets.

@c4urself
Created May 9, 2011 15:48
Show Gist options
  • Save c4urself/962761 to your computer and use it in GitHub Desktop.
Save c4urself/962761 to your computer and use it in GitHub Desktop.
Search in multiple django models
import operator
from django.db.models.query import QuerySet
from django.utils.encoding import smart_str
from django.db import models
def search_in_models(search_query, model_fields, queryset=False):
"""
Search in multiple models:
`model_fields` is are tuples within a tuple defining model_class, search_field pairs
((MyModel, ['field1', 'field2', '@field3']),
(MyOtherModel, ['field2'])
)
'@' = case insensitive search
'^' = case insensitive startswith
'=' = case insensitive exact
'' = case insensitive contains (default)
"""
def do_search(model_class, search_query, filter_fields, queryset):
qs = queryset if queryset else model_class._default_manager.all()
for bit in search_query.split():
or_queries = [models.Q(**{construct_search(
smart_str(field_name)): smart_str(bit)})
for field_name in filter_fields]
other_qs = QuerySet(model_class)
other_qs.dup_select_related(qs)
other_qs = other_qs.filter(reduce(operator.or_, or_queries))
#limit results to items wihtin the original qs seeing as the original qs could have been filtered
qs = qs & other_qs
return other_qs
def construct_search(field_name):
# use different lookup methods depending on the notation
if field_name.startswith('^'):
return "%s__istartswith" % field_name[1:]
elif field_name.startswith('='):
return "%s__iexact" % field_name[1:]
elif field_name.startswith('@'):
return "%s__search" % field_name[1:]
else:
return "%s__icontains" % field_name
final_search_result = []
for model_field_pair in model_fields:
qs = do_search(model_field_pair[0], search_query, model_field_pair[1], queryset)
for qs_item in qs:
qfinal_search_result.append(qs_item)
return final_search_result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment