简体   繁体   中英

How to return a list foreign key fields in a raw form using SerializerMethodField in Django Rest Framework?

Models:

class Author(models.Model):
  name = models.CharField()

class Book(models.Model):
  author = models.ForeignKey("Author")
  title = models.CharField()
  subtitle = models.CharField()

  def get_full_title(self):
        return "{title}: {subtitle}.".format(title=self.title, subtitle=self.subtitle)

Queryset:

queryset = Author.prefetch_related("book_set").all()

Desired Responce:

[
    {
        "id": 1,
        "name": "J. R. R. Tolkien",
        "books": [
            "The Hobbit: or There and Back Again",
            "The Fellowship of the Ring: being the first part of The Lord of the Rings.",
            "The Two Towers: being the second part of The Lord of the Rings.",
            "The Return of the King: being the third part of The Lord of the Rings."
        ]
    },
    {
        "id": 2,
        "name": "Peter Thiel",
        "books": [
            "The Diversity Myth: Multiculturalism and Political Intolerance on Campus.",
            "Zero to One: Notes on Startups, or How to Build the Future."
        ]
    }
]

The problem here is that if you use serializers.ListSerializer() on Authors.book_set list, I will get an entire model in that list with all it's fields.

If you try to use serializers.SerializerMethodField , DRF would not let you use it on multiple results. SerializerMethodField doesn't support an option many AKA serializers.SerializerMethodField(many=True) I should note here that one could write a method that cycles through the results and accumulates them into a string, but that limits you from further extending this piece of code.

PS I'm posting this question as a reference, since I couldn't find an answer anywhere else. Check the answer below for more details.

PPS I know that Tolkien wrote more than 4 books.

First, you need to create a custom list serializer with a method that will return a string with needed representation.

class AutorBookSetSerializer(serializers.ListSerializer):

    def get_custom_repr(self, obj):
        return obj.get_full_title()

Second, you need to crate your regular model serializer with a new field "books" that is not present in your initial model. Then you assign your new serializer to this field and as a child you can specify your custom SerializerMethodField call.

class AuthorSerializer(serializers.ModelSerializer):

    books = AutorBookSetSerializer(child=serializers.SerializerMethodField('get_custom_repr'))

    class Meta:
        model = Author

        fields = [
            "id",
            "name",
            "books",
        ]

Q: Why would anyone possibly ever need this?

A: If "title" field was something more complex, like a foreign key, where you would only want to get the value itself and not fetch the entire model, you could use this structure to define that logic inside of get_custom_repr method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM