Home > front end >  Cannot unpack non-iterable SerializerMethodField object in Django DRF
Cannot unpack non-iterable SerializerMethodField object in Django DRF

Time:09-02

I'm trying to assign the list from SerializerMethodField() return to a set of two fields, as:

class PostSerializer(serializers.ModelSerializer):
    postvotes, postvote = serializers.SerializerMethodField()
    
    def get_postvotes(self, obj):
        qset = PostVote.objects.filter(Q(post=obj))
        votes = [PostVoteSerializer(m).data for m in qset]
        vote = sum(vote['vote'] for vote in votes)
        return [votes, vote] # votes[], vote: Integer

However, this fails with the title-metioned error.

How can I solve this? Thanks.

CodePudding user response:

The error happens because SerializerMethodField isn't an iterable. You are not calling get_postvotes() directly to get the list which it returns.

To fix the current problem, just declare the field as normal:

postvotes = serializers.SerializerMethodField()

Then when you access the value of the postvotes field in the usual way, you will get the list of two items:

votes, vote = obj.postvotes

This change will fix the immediate error, but I'm unsure if it will work the way you intend. So here are some alternative solutions:

  1. Use a composite field instead, such as ListField or DictField

  2. Create a custom field by extending serializers.Field. See the docs for details.

Another possibility is to declare two SerializerMethodFields:

votes = serializers.SerializerMethodField()
vote_count = serializers.SerializerMethodField()

Since these are attached to methods in your serializer class, you can do whatever you want. For example, you could have a helper method to count the votes:

    def count_votes(self, obj):
        qset = PostVote.objects.filter(Q(post=obj))
        self._votes = [PostVoteSerializer(m).data for m in qset]
        self._vote_count = sum(vote['vote'] for vote in votes)

Then you can just call the method from the appropriate methods:

    def get_votes(self, obj):
        if self._votes is None:
            self.count_votes()
        return self._votes

Similarly for get_vote_count(). And be sure to initialize member variables:

    def __init__(self, *args, **kwargs):
        self.super(*args, **kwargs)
        self._votes = None
        self._vote_count = None
  • Related