Django rest framework, how to override a field so that "null is zero"

Hello I have a Model with a field that is not null on the database -the field "amount". However on the api-level this field can be skipped, which should then be stored as the default value.

I have tried several ways, however I keep coming back to the fact that to_internal_value() isn't executed for fields when the value is None? Even though the field has allow_null to be True?

class NullIsZeroDecimalField(serializers.DecimalField):
    def __init__(self, **kwargs):
        super().__init__(allow_null=True, default=0, **kwargs)

    def to_internal_value(self, data: Primitive) -> Decimal:
        """Convert None or empty strings to 0 before validation.
        :param  Primitive data: the input data

        return Decimal: decimal value
        """
        print('internal value')
        if data is None or data == "":
            return Decimal(0)
        return super().to_internal_value(data)

class ModelWithAmountSerializer(serializers.ModelSerializer):
    amount = NullIsZeroDecimalField(max_digits=20, decimal_places=10)
    class Meta:
        model = AmountModel
        fields = ("amount",)
        
def test_serializer(self):
    serializer = ReceiptLineSerializer(data={"amount": None})
    is_valid = serializer.is_valid()
    self.assertEqual(serializer.validated_data["amount"], Decimal(0))

However here the assertion fails. And upon research it turns out that the "to_internal_value" is never called for "none" fields. it is just short-circuited.

How to overcome this? And if possible, could I instead of defaulting to "0" default to the "default-of-the-model-definition"? IE as if the value is omitted when creating the model?

Instead creating custom field you can change its value during validation.

Check if this works for you

class ModelWithAmountSerializer(serializers.ModelSerializer):
    amount = DecimalField(allow_null=True, allow_blank=True, default=0, max_digits=20, decimal_places=10)
    class Meta:
        model = AmountModel
        fields = ("amount",)

    def validate_amount(self, value):
        if not value: # covers both None and ""
            value = Decimal(0)
        
        return value

But to be honest I think there is some other issue with your code that you are trying to solve in a weird way

Вернуться на верх