I have a situation where my model has a Foreign Key relationship:
# models.py
class Child(models.Model):
parent = models.ForeignKey(Parent,)
class Parent(mo
This question is a bit old but I just came across a very similar problem and managed to reduce the db calls drastically. It seems to me that Django-mptt would make things much easier for you.
One way would be to define a single model with a ForeignKey to. This way you can find out the hierarchy by it's level in the tree. For example:
class Person(MPTTModel):
parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True)
You can find out if the object is a parent by checking if Person.level = 0. If it equals 1, it's a child, 2 grandchild, etc...
Then, you could modify your code to the following:
# serializers.py
class ChildSerializer(serializers.ModelSerializer):
children = serializers.SerializerMethodField()
def get_children(self, parent):
queryset = parent.get_children()
serialized_data = ChildSerializer(queryset, many=True, read_only=True, context=self.context)
return serialized_data.data
# views.py
class ParentList(generics.ListAPIView):
def get_queryset(self):
queryset = cache_tree_children(Person.objects.all())
With this, you'll eliminate your N+1 problem. If you want to add a new ForeignKey to a genre Model, for example, you could simply modify the last line to:
queryset = cache_tree_children(Person.objects.filter(channel__slug__iexact=channel_slug).select_related('genre'))
As far as I remember, nested serializers have access to prefetched relations, just make sure you don't modify a queryset (i.e. use all()
):
class ParentSerializer(serializer.ModelSerializer):
child = serializers.SerializerMethodField('get_children_ordered')
def get_children_ordered(self, parent):
# The all() call should hit the cache
serialized_data = ChildSerializer(parent.child.all(), many=True, read_only=True, context=self.context)
return serialized_data.data
class Meta:
model = Parent
class ParentList(generics.ListAPIView):
def get_queryset(self):
children = Prefetch('child', queryset=Child.objects.select_related('parent'))
queryset = Parent.objects.prefetch_related(children)
return queryset
serializer_class = ParentSerializer
permission_classes = (permissions.IsAuthenticated,)