Serialize multiple models and send all in one json response django rest framework

15,463

Solution 1

You'll find things easier in Django REST Framework if you design your response format rationally.

It seems a bit vague at the moment, but I would suggest something like:

{
    "tweets": [
        {"tweet_attr_A": value_1, ...},  // first tweet
        {"tweet_attr_A": value_2, ...},  // second tweet
        //etc
    ],
    "articles": [
        {"id": 1, ...},  // first article
        {"id": 2, ...},  // second article
        //etc
    ]
}

We can express this with three serializers, like:

class TweetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Tweet

class ArticleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article

class TimelineSerializer(serializers.Serializer):
    tweets = TweetSerializer(many=True)
    articles = ArticleSerializer(many=True)

http://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects

Then, because we're using more than one model, it's easiest just to define your own custom viewset rather than trying to shoe-horn this into DRF's magic ModelViewSet type.
http://www.django-rest-framework.org/api-guide/viewsets/#example

First we need an object type to pass into our TimelineSerializer. It should have two attributes: tweets and articles

from collections import namedtuple

Timeline = namedtuple('Timeline', ('tweets', 'articles'))

Then we'll define the custom viewset to fetch the tweets and articles, instantiate a Timeline object and return the TimelineSerializer data:

class TimelineViewSet(viewsets.ViewSet):
    """
    A simple ViewSet for listing the Tweets and Articles in your Timeline.
    """
    def list(self, request):
        timeline = Timeline(
            tweets=Tweet.objects.all(),
            articles=Article.objects.all(),
        )
        serializer = TimelineSerializer(timeline)
        return Response(serializer.data)

Solution 2

I had a same situation to serialize multiple models to fetch filtered output from each serialize model to use from one single api. I came across this module to achieve this result.

Share:
15,463
Admin
Author by

Admin

Updated on June 15, 2022

Comments

  • Admin
    Admin almost 2 years

    This question is being asked to expand and fill in the holes from this one: Return results from multiple models with Django REST Framework

    my goal is to return a json object that I will use to dynamically populate the options in various select statements in my html code.

    so I want to grab a attribute from model a, another from model b etc

    then I want all the values from attribute a and b and c etc

    to be in a value as a JSON array to a key so

    json = {
        modelA: {'atter1, atter2, atter3}
        modelB: {'atter1, atter2, atter3}
        model..:{you get the point}
    }
    

    this part from the post referenced above makes sense:

    class TimelineViewSet(viewsets.ModelViewSet):
        """
        API endpoint that lists all tweet/article objects in rev-chrono.
        """
        queryset = itertools.chain(Tweet.objects.all(), Article.objects.all())
        serializer_class = TimelineSerializer
    

    what doesn't is this:

    class TimelineSerializer(serializers.Serializer):
        pk = serializers.Field()
        title = serializers.CharField()
        author = serializers.RelatedField()
        pub_date = serializers.DateTimeField()
    

    how do I set the the seperate model attributes to the correct json key?

    I assume its something similar to a serializer relation but these values aren't related to eachother via onetoone, onetomany, or many to many. I just want to grab all this info at once instead of creating an api for each value.

    I am a lost little girl and I am asking you to help me find my way home.

  • Admin
    Admin almost 7 years
    hey man this answer is really good and really complete. I wanted to thank you on a deeper level than I did initally. Thank you very much!!!!!
  • Anentropic
    Anentropic almost 7 years
    @amazingCarrotSoup you're welcome! I had been thinking about adding a follow-up: it occurred to me maybe in context of a 'timeline' you want all the content types returned together in a single list, ordered by date. But this becomes a data-model problem: to order them when fetching from the db you'd need to get them in a single query. And to serialize them in DRF they all need to be the same type of object. So this implies instead of separate Tweet and Article models you'd need a single TimelineItem model that could represent both types of content.
  • Admin
    Admin almost 7 years
    @Anetropic I get what your sayng and this isn't the execution I was going for but I completely understand why I would have to serialize as the same type. Again I appreciate you!
  • hammies
    hammies over 6 years
    @Anentropic do you have an example of it in timelineitem. Im trying to implement it but it's not working. stackoverflow.com/questions/47499261/…
  • Ryan Skene
    Ryan Skene about 6 years
    @Anentropic great detail. note i received an error message related to instantiating the serializer without the request context. serializer=TimelineSerializer(timeline, context={"request": request}) works.
  • Anentropic
    Anentropic about 6 years
    Oh maybe that's new in DRF v3, I've mostly used v2 which doesn't need that. Thanks for the tip!
  • Nikhil Bhardwaj
    Nikhil Bhardwaj over 4 years
    where I have to write this Timeline = namedtuple('Timeline', ('tweets', 'articles')) line
  • Anentropic
    Anentropic over 4 years
    @NikhilBhardwaj you can write it anywhere - either in the same file as the viewset, or in a separate file and then import it in the viewset module when you want to use it