how to use (from itertools import chain) to search multiple model - django

i want to search field of a multiple model in a single search view here is what i tried i know this is not a clean and better way to do that's why i am looking for a clean and better way to do it i read it is possible with (from itertools import chain) but i did not completely understand how to use it in my function based views without passing so many context here is my view
def search_item(request):
search_item = request.GET.get('search')
if search_item:
story = Story.objects.filter(Q(title__icontains=search_item)|Q(written_by__icontains=search_item))
news = News.objects.filter(Q(title__icontains=search_item)|Q(written_by__icontains=search_item))
Stock = stock.objects.filter(Q(title__icontains=search_item)|Q(written_by__icontains=search_item))
return render(request, 'search_result.html', {'ttts':ttt,'story':story,'news':news,'stock':Stock,})
thank you

You don't need itertools.chain.
def search_item(request):
results = []
search_item = request.GET.get("search")
if search_item:
q = Q(title__icontains=search_item) | Q(written_by__icontains=search_item)
for model in (Story, News, Stock):
results.extend(model.objects.filter(q))
return render(request, "search_result.html", {"results": results})
would be a simple, DRY way to write what you have.

Related

How to make search more accurate in Django?

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)

How to optimize django query filter?

Basically I have this url dispatcher that capture a search term with each word separated by + to be search in the query. I have done this this is works but I think this will hit the performance due to repeated search to the database. Is there a better way to do this?
def search(request, **kwargs):
context = RequestContext(request)
test = {}
result = BlogPage.objects.select_related('ImageMedia')
if 'search_content' in kwargs:
test['search_content'] = kwargs['search_content']
if kwargs['search_content'] != '0':
search_words = kwargs['search_content'].split('+')
for words in search_words:
result = result.filter(content__icontains=words)
context_dict = {'blog_list': result}
return render_to_response('blog/blog_search.html', context_dict, context)
You could pre-build your filter, like (untested):
from django.db import Q
search_content = kwargs.get('search_content', '')
myfilter = Q()
for term in search_content.split('+'):
myfilter |= Q(content__icontains=term)
result = BlogPage.objects.filter(myfilter).select_related('ImageMedia')
I think Python list to bitwise operations is simpler when you are using python 2.7
PS: reduce() function has been removed in Python 3, so you can't use it and it is no compatibility.reduce() has been move into module functools

Django- Many to Many field querying

I have following structure of models in django :
class BodySubPart(models.Model):
body_subpart=models.CharField(max_length=20)
def __str__(self):
return self.body_subpart
class BodyPart(models.Model):
body_part=models.CharField(max_length=20)
body_subpart=models.ManyToManyField(BodySubPart)
def __str__(self):
return self.body_part
Ex:
example,
if BodyPart=head then BodySubPart= face,mouth,eyes,nose.
if BodyPart=arm then BodySubPart= shoulder,fingers,elbow.
like this many body parts are stored.
...
now I want to create a runtime form have two choicefields (BodySubPart and BodyPart) such that when i select the BodyPart it should change the list in BodySubPart.
Ex.
The first choicefield has body parts={head,arm,chest...}
The second choice field should change when i select a particular part
If i select "head" then second choice field should show,
body sub parts={face,mouth,eyes,nose...}
Please help me here.....
What have you tried?? I think you will find people are more willing to help you if you have actually tried something yourself and not just want others to do it for you. It should go something like this:
1) BodyPart.objects.all() # all body parts
2) head = BodyPart.objects.get(body_part='head')
head_subparts = head.body_subpart.all() # all head subparts
django does a great job of explaining how to query these relationships.
https://docs.djangoproject.com/en/dev/topics/db/models/#many-to-many-relationships
In addition there are a number of really great tutorials online regarding djangos' manytomany relationships.
This requires a bit of AJAX, so first step is to create a view to handle that:
from django.core import serializers
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_list_or_404
def ajax_get_bodysubparts(request):
bodypart_id = request.GET.get('bodypart_id')
if bodypart_id:
bodysubparts = get_list_or_404(BodySubPart, bodypart__id=bodypart_id)
data = serializers.serialize('json', bodysubparts)
return HttpResponse(data, mimetype='application/json')
else:
return HttpResponseBadRequest()
Tie that to some URL in urls.py. Then, some JavaScript for your form (assumes jQuery):
$(document).ready(function(){
$('#id_bodypart').change(function(){
var selected = $(this).val();
if (selected) {
$.getJSON('/url/to/ajax/view/', {
'bodypart_id': selected
}, function (data, jqXHR) {
options = [];
for (var i=0; i<data.length; i++) {
options.append('<option value="'+data[i].id+'">'+data[i].body_subpart+'</option>');
}
$('#id_bodysubpart).html(options.join(''));
});
}
});
});
You will probably need a combination of custom form fields and widgets to get what you want.
Check out the django-ajax-filtered-fields project to see if that is close what you are looking for. It will at least provide some guidance if you decide to create your own.
You will need some javascript to make a new request to populate your fields dynamically, so that will also not be available with standard django forms.

Sorting through request.GET in Django

I want people to be able to sort things and not just me sorting things manually. So, for example, I will have a link to sorting, and the link will be something like, /?sort=issues and this would show a list of issues in alphabetical order, etc. Or /?sort=cover and it will show a list of issues with covers only.
Views.py
def issues(request):
issues_list = Issue.objects.order_by('-date_added')
paginator = Paginator(issues_list, 24)
try:
page = int(request.GET.get('page', '1'))
except ValueError:
page = 1
try:
issues = paginator.page(page)
except (EmptyPage, InvalidPage):
issues = paginator.page(paginator.num_pages)
return render_to_response('comics/issues.html', {'issues': issues}, context_instance=RequestContext(request))
So, I'd want anyone to have the option of ordering by something like -date_added, date_addded, pub_date, importance, etc.
I'd imagine I'd have to fix my views and do some request.GET magic, but I am pretty new to Django and don't really know how to go about doing this. I checked Django docs, too.
sort_by = request.GET.get('sort', '-date_added')
if sort_by not in ['-date_added', 'date_addded', 'pub_date', 'importance']:
sort_by = '-date_added'
issues_list = Issue.objects.order_by(sort_by)
Put it at the top of your view:
user_specified = request.GET.get('sort', **''**)
if user_specified:
issues_list = Issue.objects.order_by(user_specified)
else: # default ordering
issues_list = Issue.objects.order_by('-date_added')
NB: Haven't tested this!

Serializing Django Model Instances (querysets with values tag)

I am trying to serialize the following view
def headerimage(request):
service_view = list( Service_images.objects.filter(service='7'))
return render_to_response ('headerimage.html',{'service_view':service_view}, context_instance=RequestContext(request))
This is supposed to return JSON in the form shown below
{"folderList":
["with schmurps"],
"fileList":
["toto006.jpg",
"toto012.jpg",
"toto013.jpg"
]
}
However, The folder list can be one or in this case will be "7" given that is the title("folder") of the images.
After taking into account the answer below, I came up with
def headerimage(request):
service_view = Service_images.objects.filter(service='7')
image = serializers.serialize("json", service_view)
mini = list(serializers.deserialize("json", image))
return HttpResponse(image, mimetype='application/javascript')
however, I am still looking for the simplest way to do this
service_view = Service_images.objects.filter(service='7').values('image')
The problem is that the django serializer expects whole models
Service_images.objects.filter() will return a QuerySet object for you, so basically wrapping this into list() makes no sense...
Look at the docs: http://docs.djangoproject.com/en/dev/topics/serialization/#id2, and use LazyEncoder definied there.
I usually follow the below way, when the json format requirement does not match with my model's representation.
from django.utils import simplejson as json
def headerimage(request):
service_view = Service_images.objects.filter(service='7')
ret_dict = {
"folderList":
[sv.image.folder for sv in service_view],
"fileList":
[sv.image.file for sv in service_view]
}
return (json.dumps(ret_dict), mimetype="application/json")