2

I have three models like the following and have a list of Boys. How can I get a list of each of their sisters? Unfortunately I can not use prefetch related as I am stuck using a very old version of django.

class Parent(model.Model):
    name = models.CharField(max_length=50)

class Boy(model.Model):
    parent = models.ForeignKey(Parent)
    name = models.CharField(max_length=50)

class Girl(model.Model):
    parent = models.ForeignKey(Parent)
    name = models.CharField(max_length=50)

Desired output would be something like this:

{ boy1: [ sister1, sister2 ], boy2: [ .. ] }

Thanks in advance for any help!

bwrabbit
  • 529
  • 1
  • 4
  • 25
  • `prefetch_related` was added in Django 1.4. That means you're using an extremely old version of Django that has not received security updates for years. – Alasdair Apr 09 '18 at 14:54

1 Answers1

3

First, get a list of all the parents of the boys, and use that to get all the girls with those parents.

boys = Boy.objects.all()
parents = boys.values_list('parent_id', flat=True)
girls = Girl.objects.filter(parent__in=parents)

Then build a dictionary of girls keyed by parent

from collections import defaultdict
girls_by_parent = defaultdict(list)
for girl in girls:
    girls_by_parent(girl.parent_id) = girl

Then you can build your final dictionary of the sisters for each boy:

sisters = { boy.pk: girls_by_parent[boy.parent_pk] for boy in boys } 

If you could use prefetch_related, you coyld do something like:

boys = Boy.objects.select_related('parent').prefetch_related('parent__girl')
sisters = {b.pk: b.parent.girls_set.all() for b in boys}

Without prefetch_related you'll get extra queries to fetch the girls for every parent.

Alasdair
  • 298,606
  • 55
  • 578
  • 516
  • `[{b.pk: b.parent.girls_set.values_list('name', flat=True)} for b in Boy.objects.all()]` one string as an option – Brown Bear Apr 09 '18 at 14:38
  • 1
    @BearBrown if you can't use `prefetch_related`, that will lead to extra queries to fetch the girls. – Alasdair Apr 09 '18 at 14:52