ScriptAllTheThings
ScriptAllTheThings

Reputation: 41

Django Rest Framework Calculations in Serializer?

I'm working with a finance application, and due to the way that floating point math works, have decided to store all values in the database as cents (so dollar amount * 100). I have been banging my head against a wall to get the serializer to perform two calculations for me. On create/update accept a float value but then before saving to the database do value*100. Then on get, do value/100.

I got it half working using a SerializerMethodField, but that seemed to remove my ability to do create/update actions. I also at one point had something that kind of worked for create/update by changing the serializer.save() method in the view and adding an IntegerField validator on the field, but then that broke the SerializerMethodField.

In short, I'm stuck. lol

Here is my very simple model:

class Items(models.Model):
    user = models.ForeignKey(
        'CustomUser',
        on_delete=models.CASCADE,
    )
    name = models.CharField(max_length=60)
    total = models.IntegerField()

My views for this item:

class GetItems(generics.ListCreateAPIView):
    serializer_class = ItemsSerializer
    permission_classes = [permissions.IsAuthenticated, IsAuthorOrDenied]
    user = serializers.HiddenField(default=serializers.CurrentUserDefault(), )

    def get_queryset(self):
        user = self.request.user
        return Items.objects.filter(user=user)

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)


class SingleItem(generics.RetrieveUpdateDestroyAPIView):
    serializer_class = ItemsSerializer
    permission_classes = [permissions.IsAuthenticated, IsAuthorOrDenied]
    user = serializers.HiddenField(default=serializers.CurrentUserDefault(), )

    def get_queryset(self):
        user = self.request.user
        return Items.objects.filter(user=user)

    def perform_update(self, serializer):
        serializer.save(user=self.request.user)

And my serializer

class ItemsSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ('id', 'name', 'budget_total')
        model = models.Items

I feel like I should be doing more in my Serializer and less in my views, but that may be a completely different question all together.

Thanks in advance for the help!

Upvotes: 2

Views: 2979

Answers (1)

Sasja Vandendriessche
Sasja Vandendriessche

Reputation: 749

Custom Serializer Field

You could write a custom field to handle the data:

class BudgetField(serializers.Field):

    def to_representation(self, value):
        # You can decide here how you want to return your data back
        return value / 100

    def to_internal_value(self, data):
        # this will be passed to validated_data, so will be used to create/update instances
        # you could do some validation here to make sure it is a float
        # https://www.django-rest-framework.org/api-guide/fields/#raising-validation-errors
        return int(data * 100)

Then use the custom field on your serializer.

class ItemsSerializer(serializers.ModelSerializer):

    total = BudgetField()

    class Meta:
        fields = ('id', 'name', 'total')
        model = models.Items

Override .update() and .create()

You could also choose to override these methods on the serializer.

class ItemsSerializer(serializers.ModelSerializer):
    class Meta:
        fields = ('id', 'name', 'budget_total')
        model = models.Items

    def create(self, validated_data):
        # Modify validated_data with the value you need
        return super().create(validated_data)

    def update(self, instance, validated_data):
        # Modify validated_data with the value you need
        return super().update(instance, validated_data)

Upvotes: 4

Related Questions