David Rowe
David Rowe

Reputation: 61

incrementing counts of a model using a serializer

after looking through the docs for a while I haven't found a really good way to aggregate counts on a model within a serializer.

An example of this could be the standard up/down voting done on many sites.

class PostSerializer(serializers.Serializer):
    pk = serializers.IntegerField(read_only=True)
    up_vote = serializers.SerializerMethodField('cast_up_vote', write_only=True)
    down_vote = serializers.SerializerMethodField('cast_down_vote', write_only=True)
    votes = serializers.SerializerMethodField()

with models:

class Post(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)
    up_votes = models.IntegerField(default=0)
    down_votes = models.IntegerField(default=0)

lets say we wanted to perform an update on a post with up_vote set. You'd want to increment up_votes by one. What's the proper way to go about that? I'm currently experimenting with SerializerMethodFields but I'm not having much luck with using the data attributes from the passed in data. I'm also thinking that the serializerMethodField might be the wrong way to go about this since it seems more useful in going from model to representation.

Feel free to comment if more information is needed, I'm about to investigate custom fields if that's the answer.

Upvotes: 1

Views: 1060

Answers (2)

Cagatay Barin
Cagatay Barin

Reputation: 3496

You use SerializerMethodField but you didn't write any method for it. If you use SerializerMethodField, you have to write a method pointed by SerializerMethodField inside serializer. You can reach to the objects being serialized by 'obj' in that method.

Example:

class PostSerializer(serializers.Serializer):
    pk = serializers.IntegerField(read_only=True)
    up_vote = serializers.SerializerMethodField('cast_up_vote', write_only=True)
    down_vote = serializers.SerializerMethodField('cast_down_vote', write_only=True)
    votes = serializers.SerializerMethodField()

    def  cast_up_vote(self, obj):
        obj.up_vote += 1
        obj.save()

Please give the error traceback so that we could help better.

Upvotes: 1

Sphynx-HenryAY
Sphynx-HenryAY

Reputation: 766

You may use class attribute to perform such requirement. And simply write a function with name get_<field_name> as following, and the framework will link them up.

from rest_framework import serializers

class IncrSrlz( serializers.Serializer ):
    _incr = 0

    count = serializers.SerializerMethodField()

    def get_count( self, obj ):
        self._incr += 1
        return self._incr

Resulting:

>>> IncrSrlz( range(10,20), many=True ).data
[OrderedDict([('count', 1)]), OrderedDict([('count', 2)]), OrderedDict([('count', 3)]), OrderedDict([('count', 4)]), OrderedDict([('count', 5)]), OrderedDict([('count', 6)]), OrderedDict([('count', 7)]), OrderedDict([('count', 8)]), OrderedDict([('count', 9)]), OrderedDict([('count', 10)])]

Upvotes: 1

Related Questions