Django QuerySet API: How do I join iexact and icon

2019-07-23 11:16发布

问题:

I have this join:

lawyers = Lawyer.objects.filter(last__iexact=last_name).filter(first__icontains=first_name)

This is the site

If you try Last Name: Abbas and First Name: Amr it tells you that amr abbas has 1 schoolmates.

But if you try First name only it says that there are no lawyers in the database called amr (obviously there is).

If I change (last__iexact=last_name) to (last__icontains=last_name) then leaving Last Name blank works fine and amr is found.

But with last__icontains=last_name if you search for "collin" you also get "collins" and "collingwood" which is not what I want.

Do you know how I can use iexact and also have it ignored if it is blank?

Thanks

This is the view function:

def search_form(request):
    if request.method == 'POST':
        search_form = SearchForm(request.POST)
        if search_form.is_valid():
            last_name = search_form.cleaned_data['last_name']
            first_name = search_form.cleaned_data['first_name']
            lawyers = Lawyer.objects.filter(last__iexact=last_name).filter(first__icontains=first_name)
            if len(lawyers)==0:
                form = SearchForm()
                return render_to_response('not_in_database.html', {'last': last_name, 'first': first_name, 'form': form})
            if len(lawyers)>1:
                form = SearchForm(initial={'last_name': last_name})
                return render_to_response('more_than_1_match.html', {'lawyers': lawyers, 'last': last_name, 'first': first_name, 'form': form}) 
            q_school = Lawyer.objects.filter(last__icontains=last_name).filter(first__icontains=first_name).values_list('school', flat=True)
            q_year = Lawyer.objects.filter(last__icontains=last_name).filter(first__icontains=first_name).values_list('year_graduated', flat=True)
            lawyers1 = Lawyer.objects.filter(school__iexact=q_school[0]).filter(year_graduated__icontains=q_year[0]).exclude(last__icontains=last_name)
            form = SearchForm()
            return render_to_response('search_results.html', {'lawyers': lawyers1, 'last': last_name, 'first': first_name, 'form': form})
    else:
        form = SearchForm()
        return render_to_response('search_form.html', {'form': form, })

回答1:

You don't have to build the QuerySet all in one go.

lawyers = Lawyer.objects.all()
if last_name:
    lawyers = lawyers.filter(last__iexact=last_name)
if first_name:
    lawyers = lawyers.filter(first__icontains=first_name)

Django won't evaluate the QuerySet until it needs to (in this case, the len() call forces it to evaluate), so you can keep stacking filters on all days long until you're ready to run the query.

http://docs.djangoproject.com/en/dev/ref/models/querysets/#when-querysets-are-evaluated

Additionally, you don't need to create new QuerySets later on, you can just use the existing one.

q_school = Lawyer.objects.filter(last__icontains=last_name).filter(first__icontains=first_name).values_list('school', flat=True)
q_year = Lawyer.objects.filter(last__icontains=last_name).filter(first__icontains=first_name).values_list('year_graduated', flat=True)

Can be:

q_school = lawyers.values_list('school', flat=True)
q_year = lawyers.values_list('year_graduated', flat=True)