multiple filter parameters in a view - django

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)

Related

How to chain a multi-feature search in 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

Django filter testing

class BusinessPartnerFilter(SilBaseFilter):
active = django_filters.BooleanFilter(
name='date_deactivated', lookup_expr='isnull')
parent_name = django_filters.CharFilter(name='parent__name')
unmapped = django_filters.BooleanFilter(method='check_if_unmapped')
I have added the field 'unmapped' above and created the method filter below. Can someone please help me to write tests for the filter. I'm stuck.
class Meta(object):
model = models.BusinessPartner
fields = [
'name', 'bp_type', 'slade_code', 'parent', 'national_identifier',
'active', 'parent_name', 'unmapped'
]
def check_if_unmapped(self, queryset, field, value):
if value:
exclude_bps = [record.id for record in queryset if record.mapped == 0 and record.unmapped == 0]
return queryset.exclude(id__in=exclude_bps)
return queryset
You can either test the filter method in isolation, or test the evaluation of FilterSet.qs.
To test the filter method, you don't necessarily need a fully initialized FilterSet.
qs = BusinessPartner.objects.all()
f = BusinessPartnerFilter()
result = f.check_if_unmapped(qs, 'unmapped', True)
# assert something about the result
That said, it's not much more difficult to fully initialize the FilterSet and check the .qs.
qs = BusinessPartner.objects.all()
f = BusinessPartnerFilter(data={'unmapped': 'True'}, queryset=qs)
result = f.qs
# assert something about the result

Django. Contains 2 slightly different forms but second form's data being saved in both

Here are my lines dealing with the two forms :
user = request.user
user_liked = user_liked_form.save(commit = False)
user_liked.user = user
user_liked.save()
user_disliked = user_disliked_form.save(commit = False)
user_disliked.user = user
user_disliked.save()
The data submitted in second form is being saved in both liked and disliked.
I have used User foreignkey in both the liked and disliked models.
Here is the complete function :
def collect(request):
context = RequestContext(request)
submitted = False
if request.method == 'POST':
data = request.POST
user_liked_form = UserLikedForm(data = request.POST)
user_disliked_form = UserDislikedForm(data = request.POST)
# user_id = data["user_id"]
user = request.user
if user_liked_form.is_valid() and user_disliked_form.is_valid():
# user_liked_form.save(commit = True)
# user_disliked_form.save(commit = True)
user_liked = user_liked_form.save(commit = False)
user_liked.user = user
user_liked.save()
user_disliked = user_disliked_form.save(commit = False)
user_disliked.user = user
user_disliked.save()
submitted = True
else:
print user_liked_form.errors, user_disliked_form.errors
else:
user_liked_form = UserLikedForm()
user_disliked_form = UserDislikedForm()
return render_to_response(
'collect.html',
{'user_liked_form': user_liked_form, 'user_disliked_form': user_disliked_form, 'submitted': submitted},
context)
It sounds like your UserLikedForm and UserDislikedForm have the same field names and when the form is submitted, only the second value comes through in request.POST. To fix this, you will need to add a prefix to the forms:
user_liked_form = UserLikedForm(prefix='liked')
user_disliked_form = UserDislikedForm(prefix='disliked')
That way when the forms are rendered, each form will have unique field names.

Optimizing model lookups in django

I have a ToDo model, which represents a task. Each task belongs to an organization, a team within that organization, and a staff.
This is how I filter tasks, in the views:
def task_list(request, param=None, param_id=None):
if param == "org":
tasks = ToDo.objects.filter(org__id = param_id).exclude(todo_status=4)
elif param == "orgstaff":
tasks = ToDo.objects.filter(assigned_to__id = param_id).exclude(todo_status=4)
elif param == "orgteam":
tasks = ToDo.objects.filter(team__id = param_id).exclude(todo_status=4)
return render(request, "task_list.html", {"tasks":tasks})
Is there a way, I could create dicts, and replace these the above 6 lines into 1 or 2?
Using dict and dict unpacking:
def task_list(request, param=None, param_id=None):
field_mapping = {'org': 'org__id', 'orgstaff': 'assigned_to__id', 'orgteam': 'team__id'}
tasks = ToDo.objects.filter(**{field_mapping[param]: param_id}).exclude(todo_status=4)
return render(request, "task_list.html", {"tasks":tasks})

Control Query set in Django (filter,object Q)?

Base On URL
querydict = {customer_type:val1,tag:[], city:[],last_contact:valdate}
show/?customer_type=All&tag=2,3&city=3&last_contact=29/12/2009
I am going to filter by made the method:
def get_filter_result(customer_type, tag_selected, city_selected, last_contact_filled):
if customer_type=has value:
I filter by this
#queryset = Customer.objects.filter(Q(type__name=customer_type))
if tag = has value :
I filter by this
#queryset = Customer.objects.filter(Q(type__name=customer_type)
Q(type__name=tag))
if city = has value:
I filter by this
#queryset = Customer.objects.filter(Q(type__name=customer_type)
Q(type__name=tag),
Q(type__name=city))
if last_contact = has value:
I filter by this
#queryset = Customer.objects.filter(Q(type__name=customer_type)
Q(type__name=tag),
Q(type__name=city),
Q(type__name=last_contact))
Anybody Help give an idea to implement my method more simple and flexible than this?
if value of them are missing or equal None(No value is passed)
so if... else.... condition will control alots of time and code will be larger..
for example :
show/?customer_type=All&tag=&city=&last_contact=
show/?customer_type=All&tag=2,3&city=3&last_contact=29/12/2009
show/?customer_type=&tag=2,3&city=3&last_contact=
show/?customer_type=All&tag=2,3&city=&last_contact=29/12/2009
thanks
def get_filter_result(customer_type=None, tag_selected=None, city_selected=None, last_contact_filled=None):
qdict = {}
if customer_type is not None:
qdict['type__name'] = customer_type
if tag is not None:
<repeat as appropriate>
queryset = Customer.objects.filter(**qdict)
If you want to AND all your queries (like in your example), you can create dictionary of parameters, and then call filter method with this dictionary as an argument:
def get_filter_result(**kwargs):
params = {}
#delete items with empty strings
for key in kwargs:
if kwargs[key]:
params[key] = kwargs[key]
queryset = Customer.objects.filter(**params)