Reputation: 6338
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.
Upvotes: 9
Views: 3567
Reputation: 2825
get_fields
is the best way to handle this, because it will also handle your validation codepath, e.g. for errors.
If you define source
on your field, then the value of your field will read and write to the location set in source
.
class UserSerializer(serializers.ModelSerializer[User]):
"""Serialize a user to look like a JWT id token claim."""
sub = serializers.UUIDField(read_only=True, source="username")
# Will be renamed to cognito:groups
groups = serializers.ListField(
child=serializers.ChoiceField(choices=AuthGroup.choices),
source="groups", # Serializer will read and write from `groups`
)
def get_fields(self) -> dict[str, Any]:
"""Rename the groups field to cognito:groups"""
fields = super().get_fields()
fields["cognito:groups"] = fields.pop("groups")
return fields
Upvotes: 1
Reputation: 171
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
Upvotes: 2
Reputation: 88689
This is not useful when use models, but answers the use of hypens in fields, change your serializer as below
class ThingSerializer(serializers.Serializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields.update({"owner-type": serializers.CharField(write_only=True)})
Upvotes: -1
Reputation: 586
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'},
}
Upvotes: 11