How to chain a multi-feature search in Django - django

I have a 3 filter search for a job. One is for the job title/decription/company, one for job category for e.g Banking and one for the location for e.g New York
How do I chain the query such that it should render me the appropriate results if I specified any one filter and if I specified all 3 it should perform an AND. I tried doing it with if else, but it is becoming too long. Is there another way?
Here is my code:
views.py
if request.method == "POST":
internship_desc = request.POST['internship_desc']
internship_ind = request.POST['internship_industry']
internship_loc = request.POST['internship_location']
results = []
if internship_desc != "" and internship_desc is not None:
query_results = Internship.objects.filter(
Q(internship_title__icontains=internship_desc) |
Q(internship_desc__icontains=internship_desc) |
Q(recruiter__company_name__icontains=internship_desc)
)
if internship_ind !="" and internship_ind is not None:
if internship_desc != "" and internship_desc is not None:
query_results = query_results.objects.filter(
industry_type__iexact=internship_ind)
else:
query_results = Internship.objects.filter(industry_type__iexact=internship_ind)
if internship_loc !="" and internship_loc is not None:
if internship_desc != "" and internship_desc is not None and internship_ind !=""
and internship_ind is not None:
query_results = query_results.objects.filter(
industry_type__iexact=internship_ind)
query_results = query_results.objects.filter(
recruiter__company_region__iexact=internship_loc)

I think this is what you are trying to do.
result = Internship.objects.all()
if internship_desc:
result = result.filter(internship_desc__icontains=internship_desc)
if internship_ind:
result = result.filter(industry_type__iexact=internship_ind)
if internship_loc:
result = result.filter(recruiter__company_region__iexact=internship_loc)

Your best bet is using django_filters for these sort of filtering.
import django_filters
class InternshipFilter(django_filters.FilterSet):
company_name = django_filters.CharFilter(
field_name='recruiter__company_name',
lookup_expr='icontains'
)
class Meta:
model = Internship
fields = {
'internship_title': ['icontains'],
'internship_desc': ['icontains'],
}
and pass it to template like this:
context['filter_form'] = InternshipFilter().form
and use it in your view to return the filtered objects:
InternshipFilter(self.request.GET, queryset=Internship.objects.all()).qs
more info here

Related

how to merge two views functions with minor differences

I just wrote two view functions for two different models but they are very similar and only different in some names. what is the best way to merge these two view functions to prevent code repetition?
these are the view functions:
def manager_scientific(request, *args, **kwargs):
context = {}
if request.user.role == 'manager':
template = loader.get_template('reg/scientific-manager-dashboard.html')
users = User.objects.filter(role='applicant', isPreRegistered=True, scientificinfo__is_interviewed=True).order_by('-date_joined')
approved = ScientificInfo.objects.filter(is_approved__exact='0').all()
context['users'] = users
context['all_users'] = len(users)
context['approved'] = len(approved)
context['survey_choices'] = SURVEY_CHOICES
if request.GET.get('user', 'all') == 'all':
users = users
if request.GET.get('user', 'all') == 'new':
users = users.filter(scientificinfo__is_approved__exact='0')
field_list = request.GET.getlist('field')
if field_list:
if 'None' in field_list:
users = users.filter(fields__title=None) | users.filter(fields__title__in=field_list)
else:
users = users.filter(fields__title__in=field_list)
gender_list = request.GET.getlist('gender')
if gender_list:
users = users.filter(gender__in=gender_list)
education_list = request.GET.getlist('education')
if education_list:
users = users.filter(educationalinfo__grade__in=education_list)
work_list = request.GET.getlist('work')
if work_list:
users = users.filter(workinfo__position__in=work_list)
province_list = request.GET.getlist('province')
if province_list:
if 'None' in province_list:
users = users.filter(prevaddress__address_province__in=province_list) | users.filter(
prevaddress__address_province=None)
else:
users = users.filter(prevaddress__address_province__in=province_list)
query_string = request.GET.get('query_string')
if query_string:
name_query = None
for term in query_string.split():
if name_query:
name_query = name_query & (Q(first_name__contains=term) | Q(last_name__contains=term))
else:
name_query = Q(first_name__contains=term) | Q(last_name__contains=term)
users = users.filter(name_query |
Q(educationalinfo__field__contains=query_string) |
Q(educationalinfo__tendency__contains=query_string) |
Q(educationalinfo__university_name__contains=query_string) |
Q(workinfo__organization__contains=query_string) |
Q(ngoinfo__ngo_name__contains=query_string) |
Q(melli_code__contains=query_string))
users = users.distinct()
context['grade_choices'] = []
for g, grade in EDUCATIONAL_GRADE_CHOICES:
count = EducationalInfo.objects.filter(user__in=users, grade=g).count()
context['grade_choices'].append((g, grade, count))
context['work_position_choices'] = []
for p, position in WORK_POSITION_CHOICES:
count = WorkInfo.objects.filter(user__in=users, position=p).count()
context['work_position_choices'].append((p, position, count))
provinces = users.values_list('prevaddress__address_province')
provinces = Counter([d[0] for d in provinces])
context['provinces'] = provinces.items()
paginator = Paginator(users, 25) # Show 25 contacts per page.
page_number = request.GET.get('page', 1)
page_obj = paginator.get_page(page_number)
context['users'] = page_obj
context['is_interviewed'] = ScientificInfo.objects.filter(is_approved__exact='0', user__is_staff=False).count()
context['not_interviewed'] = ScientificInfo.objects.filter(is_interviewed=True, user__is_staff=False).count()
context['men'] = users.filter(gender="male").count()
context['women'] = users.filter(gender="female").count()
context['query_string'] = query_string
return HttpResponse(template.render(request=request, context=context))
and the other one:
def manager_religious(request, *args, **kwargs):
context = {}
if request.user.role == 'manager':
template = loader.get_template('reg/religious-manager-dashboard.html')
users = User.objects.filter(role='applicant', isPreRegistered=True, religiousinfo__is_interviewed=True).order_by('-date_joined')
approved = ReligiousInfo.objects.filter(is_approved__exact='0').all()
context['users'] = users
context['all_users'] = len(users)
context['approved'] = len(approved)
context['survey_choices'] = SURVEY_CHOICES
if request.GET.get('user', 'all') == 'all':
users = users
if request.GET.get('user', 'all') == 'new':
users = users.filter(religiousinfo__is_approved__exact='0')
field_list = request.GET.getlist('field')
if field_list:
if 'None' in field_list:
users = users.filter(fields__title=None) | users.filter(fields__title__in=field_list)
else:
users = users.filter(fields__title__in=field_list)
gender_list = request.GET.getlist('gender')
if gender_list:
users = users.filter(gender__in=gender_list)
education_list = request.GET.getlist('education')
if education_list:
users = users.filter(educationalinfo__grade__in=education_list)
work_list = request.GET.getlist('work')
if work_list:
users = users.filter(workinfo__position__in=work_list)
province_list = request.GET.getlist('province')
if province_list:
if 'None' in province_list:
users = users.filter(prevaddress__address_province__in=province_list) | users.filter(
prevaddress__address_province=None)
else:
users = users.filter(prevaddress__address_province__in=province_list)
query_string = request.GET.get('query_string')
if query_string:
name_query = None
for term in query_string.split():
if name_query:
name_query = name_query & (Q(first_name__contains=term) | Q(last_name__contains=term))
else:
name_query = Q(first_name__contains=term) | Q(last_name__contains=term)
users = users.filter(name_query |
Q(educationalinfo__field__contains=query_string) |
Q(educationalinfo__tendency__contains=query_string) |
Q(educationalinfo__university_name__contains=query_string) |
Q(workinfo__organization__contains=query_string) |
Q(ngoinfo__ngo_name__contains=query_string) |
Q(melli_code__contains=query_string))
users = users.distinct()
context['grade_choices'] = []
for g, grade in EDUCATIONAL_GRADE_CHOICES:
count = EducationalInfo.objects.filter(user__in=users, grade=g).count()
context['grade_choices'].append((g, grade, count))
context['work_position_choices'] = []
for p, position in WORK_POSITION_CHOICES:
count = WorkInfo.objects.filter(user__in=users, position=p).count()
context['work_position_choices'].append((p, position, count))
provinces = users.values_list('prevaddress__address_province')
provinces = Counter([d[0] for d in provinces])
context['provinces'] = provinces.items()
paginator = Paginator(users, 25) # Show 25 contacts per page.
page_number = request.GET.get('page', 1)
page_obj = paginator.get_page(page_number)
context['users'] = page_obj
context['is_interviewed'] = ReligiousInfo.objects.filter(is_approved__exact='0', user__is_staff=False).count()
context['not_interviewed'] = ReligiousInfo.objects.filter(is_interviewed=True, user__is_staff=False).count()
context['men'] = users.filter(gender="male").count()
context['women'] = users.filter(gender="female").count()
context['query_string'] = query_string
return HttpResponse(template.render(request=request, context=context))
the only differences are in model names and template addresses.
and also how can I rewrite them in a class based format?
You can create one common class that inherits from View Class and then two separate classes that inherit from the previous one. e.g
class ManagerView(View)
template_name = None
model = None
def get(self, request):
...
template = loader.get_template(self.template_name)
approved = self.model.objects.filter(is_approved__exact='0').all()
...
class ManagerReligiousView(ManagerView)
template_name = 'reg/religious-manager-dashboard.html'
model = ReligiousInfo
class ManagerScientificView(ManagerView)
template_name ='reg/scientific-manager-dashboard.html'
model = ScientificInfo
Another way with a class-based view is to emply the rarely-used ability to pass configuration options to it in the url.
class SomeView( AnyClassBasedView):
foo = 'bar'
In urls.py
path( '/do_bar/<int:pk>', SomeView.as_view(), name='do_bar'),
path( '/do_baz/<int:pk>', SomeView.as_view( foo='baz'), name='do_baz'),
And in SomeView, conditional tests on self.foo. Note, you have to declare any such parameter with a default value in the class definition before you can use it in urls.py.
A variant is to use write a view that can handle >1 method of accessing an object by interrogating self.kwargs
path( '/x/<int:pk>', X_View.as_view(), name='x_by_pk'),
path( '/x/<str:order_number>', X_View.as_view(), name='x_by_order'),
where typically the get_object method is subclassed:
class X_View( DetailView):
...
def get_object( self, queryset=None):
if 'pk' in self.kwargs:
obj = get_object_or_404( X, pk=self.kwargs['pk'] )
elif 'order_number' in self.kwargs:
obj = get_object_or_404( X, order_number=self.kwargs['order_number'] )
...
else:
raise ConfigurationError( 'Check urls.py. No valid kwarg was parsed')
return obj
NB this is a simplified get_object which ignores queryset. See the code of get_object for a better template to slot this pattern into. Classy CBVs to the rescue.

Django rest framework DjangoFilterBackend icontains not work

I have a datetime "2016-03-29T01:45:01.419731Z" in database
This is my Filter :
class TaipeiTimeFilter(filters.FilterSet):
taipei_time = django_filters.DateTimeFilter(name="update_time", lookup_type='icontains')
class Meta:
model = DataData
fields = ['taipei_time',]
class DataList(generics.ListAPIView):
queryset = DataData.objects.all()
serializer_class = DataSerializer
filter_backends = (filters.DjangoFilterBackend,)
filter_class = TaipeiTimeFilter
But when I query http://127.0.0.1:8000/api/data/?taipei_time=2016-03-29
It found nothing . What's wrong with my Filter ?
icontains is for approximate string matching. You are trying to do approximate datetime matching. For that you are going to need a range of date times. I use a custom filter that I defined for this purpose:
from django.db.models import Q
import django_filters
class RangeFilter(django_filters.Filter):
def filter(self,qs,value):
tokens = value.split('..')
if len(tokens) != 2:
return qs
(begin, end) = tokens
if begin == '' and end == '':
return qs
if begin != '' and end != '':
return qs.filter(Q(**{'%s__range'%self.name:(begin,end)}))
elif begin != '':
return qs.filter(Q(**{'%s__gte'%self.name:begin}))
elif end != '':
return qs.filter(Q(**{'%s__lte'%self.name:end}))
This is used like:
taipei_time = RangeFilter(name="update_time")
and a query looks like:
http://127.0.0.1:8000/api/data/?taipei_time=2016-03-29..2016-03-30

Django / GET form / Q filtering

I'm trying to create filter for my results that would take multiple values from html form.
This current setup gives me urls such as this /?language=French&language=German however the results would not show French and German records but only French. Additionally, adding new filtering criteria such as "level" /?language=French&level=Beginner doesn't work either.
Could anybody please help and point me in the right direction?
thanks
sikor
My form looks as follows:
RESOURCES_LANGUAGE = (('English', 'English'),
('Spanish', 'Spanish'),
('French', 'French'),
('German', 'German'))
RESOURCES_LEVEL = (('Beginner', 'Beginner'),
('Intermediate', 'Intermediate'),
('Advanced', 'Advanced'))
SORTBY = (('likes', 'Likes'),
('name', 'Name'),
('latest', 'Latest'))
class FiltersAndSortingForm(forms.Form):
language = forms.MultipleChoiceField(required=False, label='Language', widget=forms.CheckboxSelectMultiple, choices=RESOURCES_LANGUAGE)
level = forms.MultipleChoiceField(required=False, label='Level', widget=forms.CheckboxSelectMultiple, choices=RESOURCES_LEVEL)
provider = forms.ModelMultipleChoiceField(queryset=Provider.objects.all().order_by('name'), label='Provider', required=False,)
sortby = forms.MultipleChoiceField(required=False, label='Sort by', widget=forms.CheckboxSelectMultiple, choices=SORTBY)
My view:
def resources(request):
if request.GET:
language = request.GET.get('language', '')
level = request.GET.get('level', '')
provider = request.GET.get('provider', '')
sortby = request.GET.get('sortby', '')
if sortby == 'name':
orderby = 'name'
elif sortby == 'latest':
orderby = '-dt_added'
elif sortby == 'likes':
orderby = '-facebook_likes'
else:
orderby = '-facebook_likes'
qset = (
Q(language=language)
# &
# Q(level=level)
)
resources = Resource.objects.filter(inactive=0).filter(qset).order_by(orderby)
form = FiltersAndSortingForm()
else:
form = FiltersAndSortingForm()
resources = Resource.objects.filter(inactive=0).order_by('-facebook_likes')
OK, eventually after looking at this thread django dynamically filtering with q objects I got it working like this. Maybe it is not the cleanest way but seems like it is doing the job. Unless anybody could suggest better solution?
thanks
-s
def resources(request):
if request.GET:
type = request.GET.getlist('type', '')
language = request.GET.getlist('language', '')
level = request.GET.getlist('level', '')
provider = request.GET.getlist('provider', '')
sortby = request.GET.get('sortby', '')
if sortby == 'name':
orderby = 'name'
elif sortby == 'latest':
orderby = '-dt_added'
elif sortby == 'likes':
orderby = '-facebook_likes'
else:
orderby = '-facebook_likes'
qset_type = Q() # Create an empty Q object to start with
for x in type:
qset_type |= Q(provider__tags__name=x) # 'or' the Q objects together
qset_language = Q()
for x in language:
qset_language |= Q(language=x)
qset_level = Q()
for x in level:
qset_level |= Q(level=x)
qset_provider = Q()
for x in provider:
qset_provider |= Q(provider=x)
qset = qset_language & qset_level & qset_type & qset_provider
resources = Resource.objects.filter(inactive=0).filter(qset).order_by(orderby)
form = FiltersAndSortingForm()
else:
form = FiltersAndSortingForm()
resources = Resource.objects.filter(inactive=0).order_by('-facebook_likes')
I don't see additional criteria being added to the qset, that's why filtering doesn't work on those parameters. Also I would recommend that you use a dictionary for your search criteria and then unpack it. Check this question out for a detailed explanation Django: How do I use a string as the keyword in a Q() statement?. Let me know if you need any clarification.

Search in multiple columns using database query in Python/Django?

I have a model like this:
class Info(models.Model):
tape_id = models.TextField()
name = models.TextField()
video_type = models.TextField()
date = models.DateTimeField()
director = models.CharField(max_length=255)
cameraman = models.CharField(max_length=255)
editor = models.CharField(max_length=255)
time_code = models.TextField()
tag1 = models.TextField()
User can search from tape_id, name, director and cameraman using the same search input box.
I have a search view like this:
if request.method == 'POST':
search_inp = request.POST['search_box']
tape_id = Info.objects.filter(tape_id__exact=search_inp)
res = Info.objects.filter(name__icontains=search_inp)
res = Info.objects.filter(director__icontains=search_inp)
res = Info.objects.filter(cameraman__icontains=search_inp)
total_video = res.count()
if len(res) == 0 and len(tape_id) == 0 :
result = "No videos found!!"
return render_to_response('no_results_only.html', {'result':result}, context_instance=RequestContext(request))
else:
date1 = [i.date for i in res]
date = [i.strftime("%B %d, %Y") for i in date1]
a = zip(res, date)
return render_to_response('list_videos.html', {'a':a, 'total_video':total_video}, context_instance=RequestContext(request))
return HttpResponseRedirect('/')
I thought it would work at first but it doesn't. First res variable can contain the value whereas the last res be empty which will return to the no_results.html. I want to deploy this search using the same input box. How can I make this work?
First you need to import Q to use for OR filtering:
from django.db.models import Q
Then your code should look like this:
if request.method == 'POST':
search_inp = request.POST['search_box']
res = Info.objects.filter(
Q(tape_id__exact=search_inp) |
Q(name__icontains=search_inp) |
Q(director__icontains=search_inp) |
Q(cameraman__icontains=search_inp)
)
total_video = res.count()
if len(res) == 0:
result = "No videos found!!"
return render_to_response('no_results_only.html', {'result':result}, context_instance=RequestContext(request))
else:
date1 = [i.date for i in res]
date = [i.strftime("%B %d, %Y") for i in date1]
a = zip(res, date)
return render_to_response('list_videos.html', {'a':a, 'total_video':total_video}, context_instance=RequestContext(request))
return HttpResponseRedirect('/')
It is recommended to apply OR conditions in the filter. You can refer the documentation for the syntax.
Q(question__startswith='Who') | Q(question__startswith='What')
It will make sure that you will get an unique list of objects in the result.

multiple filter parameters in a view

I have a basic search view. It currently queries the db for any objects from a particular client. The view code is as follows:
def search_page(request):
form = PrdSearchForm()
prdlinks = []
show_results = True
if request.GET.has_key('query'):
show_results = True
query = request.GET['query'].strip()
if query:
form = PrdSearchForm({'query' : query})
prdlinks = \
ProjectRecord.objects.filter(client__icontains=query)
if len(prdlinks) >= 1:
records = ProjectRecord.objects.filter(client__icontains=query)
t = get_template('org_list_client.html')
html = t.render(Context({'records': records}))
return HttpResponse(html)
else:
tpl = "prd_search.html"
variables = RequestContext(request, { 'form': form,
'prdlinks': prdlinks,
'show_results': show_results})
return render_to_response(tpl, variables)
I'd like for the search field to check both for objects by client AND account. This, I think, would involve altering this code:
if query:
form = PrdSearchForm({'query' : query})
prdlinks = \
ProjectRecord.objects.filter(client__icontains=query)
to include ProjectRecord.objects.filter(account__icontains=query). Can anyone help with the syntax, or is there more involved with what I'm trying to accomplish?
I think you're looking for the Q object (as refrenced by The MYYN)
from django.db.models import Q
records=ProjectRecord.objects.filter(
Q(client__icontains=query) |
Q(account__icontains=query)
)
complex-lookups-with-q-objects
You can try to chain filters, like:
>>> ProjectRecord.objects.filter(
... client__icontains=query).filter(account__icontains=query)
This will first filter the clients, which contain the query, then filter this result queryset where the account also contains query.
General form:
>>> Entry.objects.filter(
... headline__startswith='What'
... ).exclude(
... pub_date__gte=datetime.now()
... ).filter(
... pub_date__gte=datetime(2005, 1, 1)
... )
Further useful examples are included in the documentation:
Spanning Multi-Valued Relationships
Complex lookups with Q objects
Restructured the view code to separate the client records(prdlinks) from the account records(acclinks) and handled them separately. Wasn't sure if it would work (it does) and am still not sure if this is the most efficient way to write the code (probably isn't). In any event, here is the revised code:
def search_page(request):
form = PrdSearchForm()
prdlinks = []
**acclinks = []**
show_results = True
if request.GET.has_key('query'):
show_results = True
query = request.GET['query'].strip()
if query:
form = PrdSearchForm({'query' : query})
prdlinks = \
ProjectRecord.objects.filter(client__icontains=query)
**acclinks = \
ProjectRecord.objects.filter(account__icontains=query)**
if len(prdlinks) >= 1:
records = ProjectRecord.objects.filter(client__icontains=query)
t = get_template('org_list_client.html')
html = t.render(Context({'records': records}))
return HttpResponse(html)
**elif len(acclinks) >= 1:
records = ProjectRecord.objects.filter(account__icontains=query)
t = get_template('org_list_account.html')
html = t.render(Context({'records': records}))
return HttpResponse(html)**
else:
tpl = "prd_search.html"
variables = RequestContext(request, { 'form': form,
'prdlinks': prdlinks,
'show_results': show_results})
return render_to_response(tpl, variables)