I have a few fields in my user model that are choice fields and am trying to figure out how to best implement that into Django Rest Framework.
Below is some simplified code to show what I'm doing.
# models.py
class User(AbstractUser):
GENDER_CHOICES = (
('M', 'Male'),
('F', 'Female'),
)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
# serializers.py
class UserSerializer(serializers.ModelSerializer):
gender = serializers.CharField(source='get_gender_display')
class Meta:
model = User
# viewsets.py
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
Essentially what I'm trying to do is to have the get/post/put methods use the display value of the choice field instead of the code, looking something like the below JSON.
{
'username': 'newtestuser',
'email': 'newuser@email.com',
'first_name': 'first',
'last_name': 'last',
'gender': 'Male'
// instead of 'gender': 'M'
}
How would I go about doing that? The above code does not work. Before I had something like this working for GET, but for POST/PUT it was giving me errors. I'm looking for general advice on how to do this, it seems like it would be something common, but I can't find examples. Either that or I'm doing something terribly wrong.
Django provides the
Model.get_FOO_display
method to get the "human-readable" value of a field:for the latest DRF (3.6.3) - easiest method is:
I prefer the answer by @nicolaspanel to keep the field writeable. If you use this definition instead of his
ChoiceField
, you take advantage of any/all of the infrastructure in the built-inChoiceField
while mapping the choices fromstr
=>int
:The @property override is "ugly" but my goal is always to change as little of the core as possible (to maximize forward compatibility).
P.S. if you want to
allow_blank
, there's a bug in DRF. The simplest workaround is to add the following toMappedChoiceField
:P.P.S. If you have a bunch of choice fields that all need to be mapped this, way take advantage of the feature noted by @lechup and add the following to your
ModelSerializer
(not itsMeta
):The following solution works with any field with choices, with no need to specify in the serializer a custom method for each:
Example:
given:
use:
to receive:
instead of:
Since
DRF
3.1 there is new API called customizing field mapping. I used it to change default ChoiceField mapping to ChoiceDisplayField:If You use
DefaultModelSerializer
:You will get something like:
Probalbly you need something like this somewhere in your
util.py
and import in whichever serializersChoiceFields
are involved.I found
soup boy
's approach to be the best. Though I'd suggest to inherit fromserializers.ChoiceField
rather thanserializers.Field
. This way you only need to overrideto_representation
method and the rest works like a regular ChoiceField.