Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allow hyphens in Django Rest Framework serializer field names

Given an OpenAPI specification that I'm writing code against that requires hyphen-case (aka kebab-case) variable names in request bodies, how should this be handled when using Django Rest Framework?

For example, a request POST /thing to create a Thing has this body:

{
    "owner-type": "platform"
}

But in Python, owner-type is not a valid variable name ("SyntaxError: can't assign to operator"), so instead Thing has owner_type in the model definition:

class Thing(models.Model):
    owner_type = models.CharField(max_length=8)

But now the ThingSerializer is problematic because, again, owner-type is an illegal name. This is not allowed:

    owner-type = serializers.CharField(...)

I've tried to override how the names are generated in the ModelSerializer by trying to adjust the field names generated by get_fields(), but it failed. Here's my serializer:

class ThingSerializer(serializers.ModelSerializer):
    class Meta:
        model = Thing
        fields = [
            'owner_type',
        ]

    def get_fields(self):
        fields = super().get_fields()
        out_fields = OrderedDict()
        for field_name, field in fields.items():
            out_fields[field_name.replace('_', '-')] = field
        return out_fields

And the error:

../venv/lib/python3.6/site-packages/rest_framework/fields.py:453: in get_attribute
    return get_attribute(instance, self.source_attrs)
../venv/lib/python3.6/site-packages/rest_framework/fields.py:101: in get_attribute
    instance = getattr(instance, attr)
E   AttributeError: 'Thing' object has no attribute 'owner-type'

So my question - how can I configure a DRF model serializer to allow a model's fields that contain underscore to be serialized / deserialized so that the API client sees hyphens instead of underscores? This would be a generic solution to the example above where Thing.owner_type should be read / writeable by passing the field "owner-type" in the JSON body.

I'm using latest Django and DRF on Python 3.6.

Edit 1: Clarified that ideally this would be a generic solution that translates underscores to hyphens.

like image 282
jamesc Avatar asked Apr 05 '19 11:04

jamesc


People also ask

What is Read_only true in Django?

Any 'read_only' fields that are incorrectly included in the serializer input will be ignored. Set this to True to ensure that the field is used when serializing a representation, but is not used when creating or updating an instance during deserialization.

What is the difference between ModelSerializer and HyperlinkedModelSerializer?

The HyperlinkedModelSerializer class is similar to the ModelSerializer class except that it uses hyperlinks to represent relationships, rather than primary keys. By default the serializer will include a url field instead of a primary key field.

How do you make a field optional in a serializer?

By default it is set to False. Setting it to True will allow you to mark the field as optional during "serialization". Note: required property is used for deserialization.

What is serializer method field?

Serializer fields handle converting between primitive values and internal datatypes. They also deal with validating input values, as well as retrieving and setting the values from their parent objects.


2 Answers

You can define field name with hyphen in fields and map it to correct django model field by defining source attribute in extra_kwargs - see https://www.django-rest-framework.org/api-guide/serializers/#additional-keyword-arguments

To answer you question you define ThingSerializer as bellow:

class ThingSerializer(serializers.ModelSerializer):
    class Meta:
        model = Thing
        fields = [
            'owner-type',
        ]
        extra_kwargs = {
            'owner-type': {'source': 'owner_type'},
        }
like image 184
mojeto Avatar answered Oct 09 '22 11:10

mojeto


You can use the to_internal_value of Django(See in DJango Serializer fields) to get the key with the hyphens and rename it.

Example:

class Thing(models.Model):
    owner_type = models.CharField(max_length=8)
    
    def to_internal_value(self, data):
        data['owner_type'] = data['owner-type']
        data.pop('owner-type', None)
        return data
like image 38
iradkot Avatar answered Oct 09 '22 12:10

iradkot