Kermit
Kermit

Reputation: 5992

Django filter by condition

I have a queryset that I want to paginate through alphabetically.

employees = Employee.nodes.order_by('name')

I want to compare the first letter of the employee's name name[0] to the letter that I am iterating on. - but I don't know how to filter based on conditions applied to my attribute.

employees_by_letter = []      

for letter in alphabet: 
    employees_by_this_letter = employees.filter(name[0].lower()=letter)
    employees_by_letter.append(employees_by_this_letter)

  """error -- SyntaxError: keyword can't be an expression"""

I suppose I could iterate through each employee object and append a value for their first letter... but there has to be a better way.

Upvotes: 1

Views: 856

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476699

Well this is Python, and in Python parameter names are identifiers. You can not drop some sort of expression into it.

Django has however some ways to do advanced filtering. In your case, you want to use the __istartswith filter:

employees_by_letter = [
    employees.filter(name__istartswith=letter)
    for letter in alphabet
]

This is a list comprehension that will generate such that for every letter in alphabet, the corresponding queryset is in the list.

Note however that since you eventually will fetch every element, I would actually recommend iterating (or performing a groupby for example).

Like:

from django.db.models.functions import Lower
fromiteratools import groupby

employees_by_letter = {
    k: list(v)
    for k, v in groupby(
        Employee.annotate(lowname=Lower('name')).nodes.order_by('lowname'),
        lambda x: x.lowname[:1]
    )
}

this will construct a dictionary with as key the lowercase letter (or an empty string, if there are strings with an empty name), and these all map to lists of Employee instances: the Employees with a name that start with that letter. So that means we perform only a single query on the database. Django querysets are however lazy, so in case you plan to only really fetch a few of the querysets, then the former can be more efficient.

Upvotes: 4

Related Questions