I've come up with a simple search view to search for records in my app. The user just enters all parameters in the search box then all this is matched against the database, then results are returned. One of these fields is the phone number....now in the database it's stored in the format XXX-XXX-XXX. A search, for example, for "765-4321" pull up only "416-765-4321...however I want it to return both "416-765-4321" and "4167654321"
My view is as below:
def search(request, page_by=None):
query = request.GET.get('q', '')
if query:
term_list = query.split(' ')
q = Q(first_name__icontains=term_list[0]) |
Q(last_name__icontains=term_list[0]) |
Q(email_address__icontains=term_list[0]) |
Q(phone_number__icontains=term_list[0])
for term in term_list[1:]:
q.add((Q(first_name__icontains=term) |
Q(last_name__icontains=term) |
Q(email_address__icontains=term) |
Q(phone_number__icontains=term)), q.connector)
results = Customer.objects.filter(q).distinct()
all = results.count()
else:
results = []
if 'page_by' in request.GET:
page_by = int(request.REQUEST['page_by'])
else:
page_by = 50
return render_to_response('customers/customers-all.html',
locals(), context_instance=RequestContext(request))
One option is to save stripped-down number in separate field and search by it
Related
I want to implement search on my django project. On my following queryset, with else condition its passing correct data. But with if condition whatever I search, it shows nothing.
def get_queryset(self):
category = self.request.GET['category']
query = self.request.GET['q']
if category == 'all':
products = Products.objects.filter(Q(name__icontains=query) | Q(category__name__icontains=query)).all()
else:
products = Products.objects.filter(Q(category__slug=category), Q(category__slug__icontains=self.request.GET['q']) | Q(name__icontains=self.request.GET['q']))
so on the way of learning process, I am making my first side-project on django.
I want to make my search more accurate, for example: when post body contains text with 3 words "I love stackoverflow" and someone searches for "I stackoverflow" (without word LOVE), result is not shown on the search page.
What could be the best approach in this case to get the result, even if the post body does not contain words in that order as a search query?
views.py
def search(request):
posts = Post.objects.all().order_by('title')
query = request.GET.get('q')
print(query)
if query:
posts = Post.objects.filter(
Q(title__icontains=query)|
Q(body__icontains=query)
)
context = {
"posts": posts,
}
return render(request, "search.html", context)
I'd recommend using full text search with django haystack with any search engine. But, to respond to your case, something like following would do the trick, though is not very optimised:
from django.db.models import Q
# consider only words which are having a length greater than 2
# also, words should be sanitised and cleaned before using for db queries.
# use a form for that.
parts = [i for i in request.GET.get('q').split(' ') if len(i) >= 3]
qs = Q()
query = [qs | Q(title__icontains=query) | Q(body__icontains=query) for q in parts]
result = Post.objects.filter(query).order_by().distinct()
Django provides multiple efficient ways to search on a postgreSQL database that you can find on the official docs.
Try to split your query
from itertools import chain
def search(request):
posts = Post.objects.all().order_by('title')
query = request.GET.get('q')
if query:
words = query.split(" ")
results = []
for word in words:
match = posts.filter(
Q(title__icontains=word)|
Q(body__icontains=word)
)
if match:
results.append(match)
posts = set(chain(*results))
context = {
"posts": posts,
}
return render(request, "search.html", context)
I am developing an app with Django.
I have developed a search bar to filter the database contents.
I want that, when the user clicks on the search button, the indicated content is searched in all the model fields.
My model has 16 fields, so my filter command will be a very long line, like:
selected_entries = glossary_entry.objects.filter(Q(field1__icontains=query) | Q(field2__icontains=query)) | ...ETC ETC... | Q(field16__icontains=query))
Here you can see this line in my view function, in views.py:
def glossario(request):
query = request.GET.get('q')
template = "glossario.html"
# query applied
if query:
query = request.GET.get('q')
selected_entries = glossary_entry.objects.filter(Q(field1__icontains=query) | Q(field2__icontains=query)) | ...ETC ETC... | Q(field16__icontains=query))
return render(request, template, {'all_entries':selected_entries})
# no query
else:
all_entries = glossary_entry.objects.all
return render(request, template, {'all_entries':all_entries})
Is there a shorter command to do the same?
Like:
selected_entries = glossary_entry.objects.filter(Q(ALL_MODEL_FIELDS_ICONTAINS(model=MyModel)=query))
Note: ALL_MODEL_FIELDS_ICONTAINS is my invention
You will have to specify all the fields anyway. There isn't a shortcut. But you could concatenate all fields and just do one lookup:
from django.db.models import CharField, Value as V
from django.db.models.functions import Concat
glossary_entry.objects.annotate(
search_string=Concat(
'field1', V(' '), 'field2', V(' '), 'field3', ...,
output_field=CharField()
)
).filter(search_string__icontains=q)
Note: I've added spaces between each field using Value although that's not strictly necessary with icontains, it just helps with debugging if you're looking at the value of search_string.
There is a search form. It has a drop-down list. Each item in this list has different meanings. After clicking, a search is performed, ulr looks as follows:
http://127.0.0.1:8000/search?wantbuy=kv
http://127.0.0.1:8000/search?wantbuy=ls
http://127.0.0.1:8000/search?wantbuy=km
my views.py:
kv = kv.objects.all()
ls = ls.objects.all()
km = km.objects.all()
if 'wantbuy' in request.GET:
wantbuy = request.GET['wantbuy']
if wantbuy:
queryset_list = km
Now, it doesn’t matter what value it 'wantbuy', I get all the objects from 'km'.
Tell me how to do it better? As for each url value, display your objects? I want to:
http://127.0.0.1:8000/search?wantbuy=kv
Shows all objects
kv = kv.objects.all()
etc.
Thank!
You can have several conditions to set the variable depending on the parameter
wantbuy = request.GET.get('wantbuy')
if wantbuy == 'kv':
queryset_list = kv.object.all()
elif wantbuy == 'ls':
queryset_list = ls.object.all()
elif wantbuy == 'km':
queryset_list = km.object.all()
else:
# Raise a 404 or do something else
I have a search for that looks for an alternate_id for a particular item. This search for can allow alternative if to do multiple searches if there is a comma without spaces. But I want my search form to be able to do a "partial search" as well. So searching for a*,2ndid will give all results a......,2ndid
Alternate id is a string. Not an integer. The only reason why I have as __inwas because I could perform multiple searches.
def search_item(request, client_id = 0):
client = None
if client_id != 0:
try:
client = models.Client.objects.get(pk = client_id)
except:
pass
items = None
Q_alt_ids = Q()
if request.method == 'POST':
form = forms.SearchItemForm(request.POST)
if form.is_valid():
alternate_id = form.cleaned_data['alternate_id']
items = client.storageitem_set.all()
if alternate_id:
alternate_ids= alternate_id.lower().split(",")
Q_alt_ids = Q(alternative_id__in = alternate_ids)
return render_to_response('items.html', {'items':items, 'client':client}, context_instance = RequestContext(request))
else:
HttpResponse(client)
if client is not None:
form = forms.SearchItemForm(initial = {'client':client.pk})
else:
form = forms.SearchItemForm()
return render_to_response('search_items.html', {'form':form}, context_instance = RequestContext(request))
Aside from writing your own SQL, I think the only out of the box solution that can handle a robust query like a wildcard is a regular expression. If the wildcard is always in the same place, such as followed by a partial string, you could use the field__startswith=partial lookup type.
But in general, wildcards = regex
http://docs.djangoproject.com/en/dev/ref/models/querysets/#iregex
BUT,
I have a sneaking suspicion you might be looking for the contains or icontains filter lookup type.
If you want to match 'hello', 'ello', 'world' in 'hello world':
# first split the string by comma
queries = form.cleaned_data.get('query', '').split(',')
# populate queries list
query_list = [Q(**{'alternative_id__icontains' : query }) for query in queries]
# join queries list with the OR operator
results = MyModel.objects.filter( reduce(operator.or_, query_list) )
thanks yuji for the answer. These change are needed to make it work.
return_all = False
filters = []
if alternate_id:
alternate_ids = alternate_id.lower().split(",")
for item in alternate_ids:
if '*' not in item:
filters.append( Q(alternative_id=item) )
elif item == '*':
return_all = True
elif item.startswith('*') and item.endswith('*'):
filters.append( Q(alternative_id__icontains=item.strip('*')) )
elif item.startswith('*'):
filters.append( Q(alternative_id__endswith=item.strip('*')) )
elif item.endswith('*'):
filters.append( Q(alternative_id__startswith=item.strip('*')) )
else:
assert False, "Wildcard in invalid position (valid is.. *XX, *XX*, XX*)"
if not filters or return_all:
items = items.all()
else:
items = items.filter(reduce(operator.or_, filters))