13

i have a django app that retrieve all subjects from a single table of users. i've also implemented an input search form, this is the query performed:

all_soggs =     Entity.objects.filter(lastname__istartswith=request.GET['query_term']).order_by('lastname')
if(all_soggs.count()==0):
    all_soggs = Entity.objects.filter(firstname__istartswith=request.GET['query_term']).order_by('firstname')

as you can see the query first search for matching items by lastname, and then by firstname. this works until i insert the complete name 'firstaname lastname' or 'lastname firstname', in this case there's no results. how can i modify the query to make a better search?

thanks - luke

frederj
  • 1,483
  • 9
  • 20
Luke
  • 1,794
  • 10
  • 43
  • 70

4 Answers4

16

Copy/paste from: https://stackoverflow.com/a/17361729/1297812

from django.db.models import Q 

def find_user_by_name(query_name):
   qs = User.objects.all()
   for term in query_name.split():
     qs = qs.filter( Q(first_name__icontains = term) | Q(last_name__icontains = term))
   return qs
Community
  • 1
  • 1
laffuste
  • 16,287
  • 8
  • 84
  • 91
7

You need Q objects and you also need to split your query into separate terms (since no first name will match the full string "Firstname Lastname").

Here's an idea to match any first or last name starting with either "Firstname" or "Lastname" in the search "Firstname Lastname".

This is a generic search - adjust the query to suit your specific needs!

Edit: oops, I really don't like using reduce since it looks confusing, but these need to be ORed together and we can't do a more verbose version because the number of terms is unknown.

import operator
from django.db.models import Q

search_args = []
for term in request.GET['query_term'].split():
    for query in ('first_name__istartswith', 'last_name__istartswith'):
        search_args.append(Q(**{query: term}))

all_soggs = Entity.objects.filter(reduce(operator.or_, search_args))

To clarify how to use Q objects, given the search "Firstname Lastname" the previous query is equal to:

Entity.objects.filter(
    Q(first_name__istartswith="Firstname") | Q(last_name__istartswith="Firstname") |
    Q(first_name__istartswith="Lastname") | Q(last_name__istartswith="Lastname")
    )
Yuji 'Tomita' Tomita
  • 115,817
  • 29
  • 282
  • 245
2

This is a fairly old question but I just ran into the same problem and I thought I would share a more elegant solution.

from django.db.models import Value as V
from django.db.models.functions import Concat

from ..models import User

def find_user_by_name(search_str) -> QuerySey[User]:
    q = User.objects.annotate(full_name=Concat('first_name', V(' '), 'last_name'))
    q = q.filter(full_name__icontains=search_str)
    return q

This is the only solution that I have found which lets gives the behaviour I wanted, IE seaching with a full name string with a space ("John Doe") and with a partial string ("John Do").

tomcheney
  • 1,750
  • 1
  • 17
  • 33
0

Similar question:Querying full name in Django

query = request.GET.get('query')
entities = []

try:
    firstname = query.split(' ')[0]
    lastname  = query.split(' ')[1]
    entities += Entity.objects.filter(firstname__icontains=firstname,lastname__icontains=lastname)
    entities += Entity.objects.filter(firstname__icontains=lastname,lastname__icontains=firstname)

entities = set(entities)
atx
  • 73
  • 1
  • 8
  • What's the point of answering the question twice and referencing your own answer? – Omri Luzon May 26 '17 at 19:08
  • 1
    According to the rules of stack overflow you have to give complete answer even if you are referencing a link, because links can go broke – atx May 26 '17 at 19:13