Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework - Serializer Method field

When using serializer method field, the dictionary with Decimals is converted to Integers.

For eg.

class BillSerializer(serializers.ModelSerializer):
    bill_details = serializers.SerializerMethodField()

    class Meta:
        model = Bill
        fields = ('__all__')

    def get_bill_details(obj):
        return {'editable': False,
           'final_amt': Decimal('4198.00'),
           'total_amt': Decimal('4198.00'),
        }

becomes this:

"bill_details": {
    "total_amt": 4198,
    "editable": false,
    "final_amt": 4198
  }

Is there any solution to this? I am expecting this:

"bill_details": {
    "total_amt": "4198.00",
    "editable": false,
    "final_amt": "4198.00"
}
like image 256
Anuj Avatar asked Aug 09 '16 11:08

Anuj


People also ask

What is serializer method field in Django?

Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.

What is To_representation in Django?

to_representation(self, value) method. This method takes the target of the field as the value argument, and should return the representation that should be used to serialize the target. The value argument will typically be a model instance.

How do I pass Queryset to serializer?

How do I pass Queryset to serializer? To serialize a queryset or list of objects instead of a single object instance, you should pass the many=True flag when instantiating the serializer. You can then pass a queryset or list of objects to be serialized.


2 Answers

Django REST framework 3.0 gives you the option to serialize decimals as floats.

Decimals are now coerced to strings by default in the serialized output. You can modify this behavior globally by using the COERCE_DECIMAL_TO_STRING settings key.

REST_FRAMEWORK = {
    'COERCE_DECIMAL_TO_STRING': False
}

Or modify it on an individual serializer field, using the coerce_to_string keyword argument.

# Return `Decimal` instances in `serializer.data`, not strings.
amount = serializers.DecimalField(
    max_digits=10,
    decimal_places=2,
    coerce_to_string=False
)

The default JSON renderer will return float objects for un-coerced Decimal instances. This allows you to easily switch between string or float representations for decimals depending on your API design needs.

UPDATE

If you want to just round it to produce a string with 2 decimal precision to display to the user, you can do

def get_bill_details(obj):
    return {
       'editable': False,
       'final_amt': '%.2f' %  Decimal('4198.00'),
       'total_amt': '%.2f' %  Decimal('4198.00'),
    }

UPDATE 2

You can make it DRY, By using a custom JSONEncoder class.

Create a DecimalEncoder class, inheriting json.JSONEncoder

from decimal import Decimal
import json

class DecimalEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Decimal):
            return '%.2f' % obj
        return json.JSONEncoder.default(self, obj)

Now Use this class inside get_bill_details method

def get_bill_details(self, obj):
    bill_detail = {
        'editable': False,
        'final_amt': Decimal('4198.00'),
        'total_amt': Decimal('4198.00'),
    }
    return json.loads(json.dumps(bill_detail, cls=DecimalEncoder))
like image 132
Satendra Avatar answered Sep 28 '22 07:09

Satendra


You can use new Serializer in SerializerMethodField like so:

from rest_framework import serializers

class BillDetailsSerializer(serializers.Serializer):
    editable = serializers.BooleanField()
    final_amt = serializers.DecimalField(decimal_places=2, max_digits=15)
    total_amt = serializers.DecimalField(decimal_places=2, max_digits=15)

class BillSerializer(serializers.ModelSerializer):
    bill_details = serializers.SerializerMethodField()

    class Meta:
        model = Bill
        fields = '__all__'

     def get_bill_details(self, obj):
         data = {...} # your logic
         field_serializer = BillDetailsSerializer(data=data)
         field_serializer.is_valid() # this serializer must always valid
         return field_serializer.data

I think it is more clear and everyone can undestand what fields have bill_details

like image 26
Andrey Berenda Avatar answered Sep 28 '22 09:09

Andrey Berenda