For example, let's say I have the following models:
class Group(models.Model):
group_name = models.CharField(max_length=20)
class Person(models.Model):
group = models.ForeignKey(Group)
name = models.CharField(max_length=50)
I want to list all groups, and for each group list the people in the group.
Group A: Person1, Person2, Person3
Group B: Person4, Person5, Person6
I get stuck at Group.objects.all(), which will only return a queryset containing the Group objects that I can cycle through in the template. I don't know how to cycle through the people in each group though. Help?
groups = Group.objects.all()
{% for g in groups %}
g.group_name:
<< Need an inner loop here to cycle through the people in each group? >>
{% endfor %}
{% for p in g.person_set.all %}
You can use the builtin regroup
tag:
Template:
{% regroup people by group as people_by_group %}
{% for group in people_by_group %}
{{ group.grouper.group_name }}
{% for person in group.list %}
{{ person }}
{% endfor %}
{% endfor %}
Context:
{'people': Person.objects.all().select_related('group').order_by('group')}
This won't list empty groups, but you can build a similar strucutre in your own view, for example:
groups = list(Group.objects.all())
groups_map = dict((g.pk, g) for g in groups)
for g in groups:
g.person_cache = []
for person in Person.objects.all():
if person.group_id is not None:
groups_map[person.group_id].person_cache.append(person)
del groups_map
# ``groups`` now contains a list suitable for your template
This way you make only two queries. Using a related manager in a loop will produce number_of_groups+1
queries.
You need to actually query for the people instead of the group.
people = Person.objects.select_related().order_by('group')
{% for person in people %}
{% ifchanged person.group %}
{{ person.group }}
{% endifchanged %}
{{ person }}
{% endfor %}
This does one query for all people and their related groups, ordered by group. The ifchanged in the template recognizes when you've moved on to a new group and prints it out.
Hope that helps.