Search code examples
pythondjangodjango-modelsdjango-rest-frameworkmany-to-many

How to show only a few Many-to-many relations in DRF?


If for an example I have 2 models and a simple View:

class Recipe(model.Model):
    created_at = model.DateField(auto_add_now=True)


class RecipeBook(model.Model):
    recipes = model.ManyToManyField(Recipe)

...

class RecipeBookList(ListAPIView):
    queryset = RecipeBook.objects.all()
    serializer_class = RecipeBookSerializer

...

class RecipeBookSerializer(serializers.ModelSerializer):
    recipe = RecipeSerializer(many=True, read_ony=True)
    
    class Meta:
        model = RecipeBook
        fields = "__all__"

What would be the best way, when showing all Restaurants with a simple GET method, to show only the first 5 recipes created and not all of them?


Solution

  • QuerySet way:

    You can specify custom Prefetch operation in your queryset to limit the prefetched related objects:

    queryset.prefetch_related(Prefetch('recipes', queryset=Recipe.objects.all()[:5]))
    

    Docs: https://docs.djangoproject.com/en/3.2/ref/models/querysets/#prefetch-objects

    Serializer way:

    You can use source to provide a custom method to return a custom queryset

    class RecipeBookSerializer(serializers.ModelSerializer):
        recipes = RecipeSerializer(many=True, read_only=Treue, source='get_recipes')
    
        class Meta:
            model = RecipeBook
            fields = "__all__"
    
        def get_recipes(self, obj):
            return obj.recipes.all()[:5]
    

    Then use prefetch_related("recipes") to minimize related queries.

    Source: django REST framework - limited queryset for nested ModelSerializer?

    The problem with the serializer way is that either a related query for recipes is performed per recipe book object or all related recipes are pre-fetched from the beginning.