How can you deal with a reserved word (from) in a serializer in DRF

Viewed 204

Below is a serializer with a reserved word (from) used in a field:

class EdgeSerializer(serializers.Serializer):
    field_name_map = {
        '_from': 'from'
    }
    class Meta:
        read_only_fields = ('_from', 'to', 'weight')
    _from = serializers.IntegerField(required=True)
    to = serializers.IntegerField(required=True)
    weight = serializers.FloatField(required=True)

The problem is when I read serializer.data the field name in the "output" is _from, not from. I've tried using source=, I've tried reversing the field_name_map (key, value) => (value, key).

I can't use from as the name of the field, python blows up trying to parse the file

So, I did this which works, but which leaves a bad taste in my mouth and seems the "wrong" way to do it:

class FromIntegerField(serializers.IntegerField):
    def bind(self, field_name, parent):
        super().bind(field_name, parent)
        self.field_name = 'from'

class EdgeSerializer(serializers.Serializer):
    _from = FromIntegerField(required=True)
    to = serializers.IntegerField(required=True)
    weight = serializers.FloatField(required=True)

    class Meta:
        read_only_fields = ('_from', 'to', 'weight')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        #self.fields (a `BindingDict`) contains a field keyed by '_from' 
        #  to the field where I changed the field_name to 'from'

While this works, the smell is a little bit too much for me and I'd like to know the right way to do this. I could generalize this approach to allow me to pass an output_name='from' but that still seems wrong. I would also like to be able to take a dict that has a key of from rather than _from as well but that is a secondary concern. I think I can do it by changing self.fields to be keyed off of 'from' instead of '_from' or setting the source to 'from' in the FromIntegerField class

1 Answers

In my opinion it is quite ugly but likely works, you can override the .get_fields(…) method

class EdgeSerializer(serializers.Serializer):
    _from = FromIntegerField(required=True)
    # …

    def get_fields(self):
        result = super().get_fields()
        _form = result.pop('_from', None)
        result['from'] = _from
        return result

here we thus map the original key-value pairs to a slightly different dictionary where we add this to the from key.

Related