André
André

Reputation: 25584

Django-Rest-Framework - nested objects and serializers, how to?

I'm using DRF for the first time. I've been reading the documentation pages but no clue on how to do this.

I have two models, the AdPrice Model make reference to the Ad Model. I need to list the various prices for an Ad.

My question is: How can I get a list of Ads like this?

[
    {
    "title": "Some long title for Ad1",
    "name": "Name Ad1",
    "prices": {
                  { "name": "price now", "price": 200 }, 
                  { "name": "price later", "price": 300 }
              }
    },
]

models.py

class Ad(models.Model):
    title = models.CharField(max_length=250)
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

class AdPrice(models.Model):
    ad = models.ForeignKey(Ad)
    name = models.CharField(max_length=50)
    price = models.DecimalField(max_digits=6, decimal_places=2)

    def __str__(self):
        return self.name

serializers.py

class AdPriceSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=50)
    price = serializers.DecimalField(max_digits=6, decimal_places=2)

    class Meta:
        model = AdPrice

class AdSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=250)
    name = serializers.CharField(max_length=100)
    prices = AdPriceSerializer(many=True)

views.py

class AdViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = Ad.objects.all().order_by('-date_inserted')
    serializer_class = AdSerializer

When I try the code above I got this error:

AttributeError at /ads/

Got AttributeError when attempting to get a value for field `prices` on serializer `AdSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Ad` instance.
Original exception text was: 'Ad' object has no attribute 'prices'.

Any clues on how to solve this?

Best Regards, André Lopes.

Upvotes: 2

Views: 1944

Answers (1)

Borko Kovacev
Borko Kovacev

Reputation: 1020

From what I am seeing here you're missing the read_only=True and class meta within the

class AdSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=250)
    name = serializers.CharField(max_length=100)
    prices = AdPriceSerializer(many=True, read_only=True)

    class Meta:
        model = Ad

and in order to also avoid n+1 query problem you'd want to override the queryset to be

from django.db.models import Prefetch

queryset=Ad.objects.all().order_by('-date_inserted').prefetch_related(Prefetch('adprice_set', queryset=AdPrice.objects.filter(ad_id__in=queryset), to_attr='prices'))

Since Django has a lazy ORM, it means that for every Ad you have in there, it would make another query to get the AdPrices. So for 100 ads, it would make 200 queries. Not the most efficient solution, right?. With prefetch you're making that as short as only two queries, one to get all the ads and the other one to get all the relevant adprices.

Upvotes: 2

Related Questions