Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return exact matches at top of Django Queryset

I have a django model, called "User" which stores some basic information about people, namely first and last name. I currently have a simple search across my django model where, if a user types in a first name, The django queryset returns the first 10 matches, ordered by last name.

For example, currently, if you search for "Sam" you might get the following results:

  1. Sam Abbott
  2. Samuel Baker
  3. Sammy Rogers
  4. Sam Simmons

The code for this is simple:

User.objects.filter(Q(first__istartswith=token)).order_by('last')

However, I want to alter this so that any exact first name matches are returned first, followed by the rest of the results. So if someone types in "Sam", the results should instead be:

  1. Sam Abbott
  2. Sam Simmons
  3. Samuel Baker
  4. Sammy Rogers

(Exact first name matches first, sorted by last name, followed by the rest of the matches sorted by last name).

I thought about turning this into 2 querysets and then just combining the lists, but I was wondering if it was possible to do this in 1 query, ideally sticking with the basic django queryset API (rather than writing a one-off query). Does anyone know a way to do that?

Thanks in advance.

like image 742
Chad Avatar asked Jul 10 '12 05:07

Chad


1 Answers

I don't think it's really possible to do that with only one query (at least with Django ORM).

So your 2 queries should look like this:

limit = 10
q1 = User.objects.filter(first__iexact=token).order_by('last')[:limit]
limit -= len(q1)
if limit:
    q2 = User.objects.exclude(pk__in=q1).filter(first__istartswith=token).order_by('last')[:limit]
else:
    q2 = []
users = list(q1) + list(q2)

Another way to do it is to filter the query in python but you will have to get all the results, not only the last 10:

query = User.objects.filter(first__istartswith=token).order_by('last')
exacts = [user for user in query if user.first == token]
others = [user for user in query if user.first != token]
users = exacts + others
like image 106
Etienne Avatar answered Oct 20 '22 21:10

Etienne