2

In Django, what is the most efficient way to create a nested dictionary of data from querying related and child models?

For example, if I have the following models:

  • Parent
    • Children
      • Pets

I've seen django's model_to_dict method, and that's pretty cool, so I imagine I could loop through each level's queryset and create a bunch of DB calls on each level, for each instance, but is there a better way?

For example, could "prefetch_related" be used to get all three tiers as it is used to get two tiers here?

It would be great to get the dictionary to look something like this:

[
  {
    "name": "Peter Parent",
    "children": [
      {
        "name": "Chaden Child",
        "pets": [
          {
            "name": "Fanny",
            "type:": "fish"
          },
          {
            "name": "Buster",
            "type:": "bunny"
          }
        ]
      },
      {
        "name": "Charlete Child",
        "pets": [
          {
            "name": "Dandy",
            "type:": "dog"
          }
        ]
      }
    ]
  }
]

Edit:

By request this is what the models could look like:

class Pet(models.Model):
    name = models.CharField(max_length=50)
    type = models.CharField(max_length=50)

    def __str__(self):
        return self.name

class Child(models.Model):
    name = models.CharField(max_length=50)
    pets = models.ManyToManyField(Pet)

    def __str__(self):
        return self.name

class Parent(models.Model):
    name = models.CharField(max_length=50)
    children = models.ManyToManyField(Child)

    def __str__(self):
        return self.name

And this is what the raw sql would look like:

SELECT pa.name, ch.name, pe.name, pe.type
FROM aarc_parent pa
JOIN aarc_parent_children pc ON pc.parent_id = pa.id
JOIN aarc_child ch ON ch.id = pc.child_id
JOIN aarc_child_pets cp ON cp.child_id = ch.id
JOIN aarc_pet pe ON pe.id = cp.pet_id
Community
  • 1
  • 1
aero
  • 1,654
  • 1
  • 21
  • 31

1 Answers1

4

You can use prefetch_related along with list comprehensions. prefetch_related will help in avoiding extra queries every time related object is accessed.

parents = Parent.objects.all().prefetch_related('children__pets')

[{'name': parent.name, 'children': [{'name': child.name, 'pets': [{'name':pet.name, 'type':pet.type} for pet in child.pets.all()]} for child in parent.children.all()]} for parent in parents]
Rahul Gupta
  • 46,769
  • 10
  • 112
  • 126
  • Thanks Rahul for your answer. When testing it I am getting the following error: AttributeError: Cannot find 'childs' on Parent object, 'childs__pets' is an invalid parameter to prefetch_related() – aero Jun 04 '16 at 15:29
  • 1
    Please try with `children__pets`. – Rahul Gupta Jun 04 '16 at 15:30
  • 1
    Ha, I just was about to post that "children_pets" worked. Thanks for you help Rahul, this is what I need! – aero Jun 04 '16 at 15:33