Currently the ChoiceField serializes to_native through its base class which causes it to return the first of the choice tuple. This is what Django does. There should be an option to use the "get_FOO_display"-function type of behaviour (https://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.get_FOO_display) to get ChoiceFields to serialize the human-readable form in native.
Could you expand on this from the user point-of-view.
Ie: Give a simple ChoiceField example, then show:
Then present what you'd instead like the behaviour to be in each of those three cases?
That'd be tremendously helpful! :)
I ended up writing a very simple subclass of ChoiceField that outputs what I needed.
class NewChoiceField(ChoiceField):
def field_to_native(self, obj, field_name):
return getattr(obj,'get_'+field_name+'_display')()
To elaborate what I meant with my previous one:
class FoodModel(models.Model):
food_choices = (('pasta','Delicious Pasta'), ('pizza', 'Big pizza'))
food = CharField(choices=food_choices)
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = FoodModel
fields = ("food")
Current output:
{
'food': 'pasta'
}
What I wanted:
{
'food': 'Delicious Pasta'
}
Don't know how it could be included as an option perhaps to display either the actual value or the human-readable name (https://docs.djangoproject.com/en/dev/ref/models/fields/#choices)
Unlike with purely HTML based forms where the presentation may not match what's stored at the data layer, I would always expect the output for a choice in the API to match the input (at least for a standard ChoiceField).
I'd be willing to look at changing the behaviour of what is presented in the browsable API (ie the display of the dropdown), but I'm not convinced by allowing the API input and API output to differ. (Though further examples of custom field types in the docs wouldn't be a bad idea?)
Closing this off on the basis of... "I'd be willing to look at changing the behaviour of what is presented in the browsable API (ie the display of the dropdown), but I'm not convinced by allowing the API input and API output to differ."
Willing to consider alternate issues/pull requests along those lines.
I would also prefer the readable form of choices. Giving an integer back makes me go "wtf does that mean"?
Matching input vs output is irrelevant; only the business requirement should be considered.
At least add an argument to the ChoiceField to overwrite label over key for the return value. If you want to return the key value then the value object should be returned as a nested object with its properties, but single field representations can just as well be the label thereof.
Pull requests to set this as an option would be considered. Not particularly motivated to push this on the core team otherwise.
I use django-enumfields with a custom DRF field for this:
from enum import Enum
from django.db import models
from enumfields import EnumIntegerField
class GeoModel(models.Model):
class LocationType(Enum):
ROOFTOP = 1
RANGE_INTERPOLATED = 2
GEOMETRIC_CENTER = 3
APPROXIMATE = 4
UNRESOLVED = 5
location_type = EnumIntegerField(
enum=LocationType,
default=LocationType.UNRESOLVED
)
...
from rest_framework import serializers
class EnumField(serializers.ChoiceField):
def __init__(self, enum, **kwargs):
self.enum = enum
kwargs['choices'] = [(e.name, e.name) for e in enum]
super(EnumField, self).__init__(**kwargs)
def to_representation(self, obj):
return obj.name
def to_internal_value(self, data):
try:
return self.enum[data]
except KeyError:
self.fail('invalid_choice', input=data)
from rest_framework import serializers
from . import fields
from . import models
class GeoModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.GeoModel
location_type = fields.EnumField(enum=models.GeoModel.LocationType)
Thanks man a easy way to solve my problem...
Most helpful comment
I use django-enumfields with a custom DRF field for this:
models.py
fields.py
serializers.py