Reputation: 41
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
Reputation: 749
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
.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