Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Split queryset or get multiple querysets by field instead of just ordering by field

I want to query a database in a way such that rather than just ordering by some field, I get a separate QuerySet (or dictionary, list, whatever) for each unique value of that field. Hopefully the below example will help:

Suppose a model like

Class Person(models.Model):
   first_name = models.CharField()
   last_name = models.CharField

Calling Person.objects.all().order_by('last_name') gives me a single long QuerySet. I want instead to have a separate list for each unique last_name. So one list for every Person with last_name="Smith" and another list for every Person with last_name="Nguyen" etc.

Obviously I can't know ahead of time what last_names will be in the database, nor how many people will share a common last_name. Is there any fast, efficient, or automatic way to do this in django or do I just need to process the data myself after getting the one large queryset back?

like image 918
Tango Avatar asked Jul 09 '12 01:07

Tango


People also ask

How do I combine QuerySet?

You can also use the chain() method from the Itertools module, which allows you to combine two or more QuerySets from different models through concatenation. Alternatively, you can use union() to combine two or more QuerySets from different models, passing all=TRUE if you want to allow duplicates.

What is Select_related in Django?

Django offers a QuerySet method called select_related() that allows you to retrieve related objects for one-to-many relationships. This translates to a single, more complex QuerySet, but you avoid additional queries when accessing the related objects. The select_related method is for ForeignKey and OneToOne fields.


2 Answers

samy's code is correct, but pretty inefficient: you do n+1 queries (where n is the number of unique last names). Since you're going to be getting all the objects in the table anyway, you might as well do it in one go. Use this instead:

from collections import defaultdict
person_dict = defaultdict(list)
persons = Person.objects.all()
for person in persons:
    person_dict[person.last_name].append(person)
like image 183
Daniel Roseman Avatar answered Nov 15 '22 21:11

Daniel Roseman


Look at the regroup tag.

{% regroup persons by last_name as surname_list %}

<ul>
{% for last_name in surname_list %}
    <li>{{ last_name.grouper }}
    <ul>
        {% for person in last_name.list %}
          <li>{{ person.last_name }}, {{ person.first_name }}</li>
        {% endfor %}
    </ul>
    </li>
{% endfor %}
</ul>

(this code is untested, please read the docs and fix any problem yourself)

like image 29
Paulo Scardine Avatar answered Nov 15 '22 23:11

Paulo Scardine