Reputation: 2092
I have a model called Ticket
with a choice field status
:
STATUS_CHOICES = (
('1', 'Open'),
('2', 'Resolved'),
('3', 'Won\'t fix')
)
status = models.CharField(
"Status",
max_length=1,
choices=STATUS_CHOICES,
blank=False,
default='1'
)
I am using Django rest framework for the API. The default model serializer sends the first value of choice i.e. 1
instead of Open
. How can I make it to send the second text value in response.
Here's a part of my Serializer:
class TicketSerializer(serializers.ModelSerializer):
status = serializers.ChoiceField(choices=Ticket.STATUS_CHOICES)
class Meta:
model = Ticket
fields = ('status', )
Upvotes: 0
Views: 1574
Reputation: 2092
Following approach solves this problem. We can create a custom field for Choices.
class ChoicesField(serializers.Field):
def __init__(self, choices, **kwargs):
self._choices = choices
super(ChoicesField, self).__init__(**kwargs)
def to_representation(self, obj):
return self._choices[int(obj) - 1] # obj is the first value
def to_internal_value(self, data):
return getattr(self._choices, data)
This is similar to the solution suggested by Tahir but in a cleaner way.
Upvotes: 1
Reputation: 5184
API's are generally used to fetch data, not data's UI representation. In ('1', 'Choice 1')
, '1'
is the value to be stored and used everywhere, 'Choice 1'
is its UI representation that we need to explicitly use when we need it (in the UI i.e. templates).
DjangoAdmin is a complete Django app so it uses the UI representation explicitly. If you use Django Forms you need to use it explicitly too. It is not automatically used.
In template if you do {{ my_form_choice_field.value }}
it will use '1'
not 'Choice 1'
. You need to explicitly use {{ my_form_choice_field.get_my_form_choice_field_display }}
(in the UI layer) to get 'Choice 1'
.
So, as I started with API's are generally used to fetch data, not it's UI representation. That is why (JUST LIKE DJANGO) DjangoRestFramework also uses only the original value and not its UI representation.
Now to answer your question, if you really want to return the UI representation. Then you need to override BaseSerializer's to_representation
method to replace value with its UI representation and to override to_internal_value
to replace UI representation back to value when saving.
You can see an example (from the docs) here.
Quoting the docs example code linked above here.
class HighScoreSerializer(serializers.BaseSerializer):
def to_internal_value(self, data):
score = data.get('score')
player_name = data.get('player_name')
# Perform the data validation.
if not score:
raise ValidationError({
'score': 'This field is required.'
})
if not player_name:
raise ValidationError({
'player_name': 'This field is required.'
})
if len(player_name) > 10:
raise ValidationError({
'player_name': 'May not be more than 10 characters.'
})
# Return the validated values. This will be available as
# the `.validated_data` property.
return {
'score': int(score),
'player_name': player_name
}
def to_representation(self, obj):
return {
'score': obj.score,
'player_name': obj.player_name
}
def create(self, validated_data):
return HighScore.objects.create(**validated_data)
Upvotes: 4