Django REST API无法返回ChoiceField可读值,POST接口报类型不匹配
Hey there! Let's sort out this issue you're facing with Django REST Framework and your choice field. The problem makes total sense: when you tried using get_status_display directly in your serializer, it worked for GET requests (since we're just fetching and formatting data), but threw that "type mismatch" error on POST because DRF didn't know how to convert the human-readable string back to the raw value your model expects.
Here are two practical solutions depending on your needs:
Option 1: Keep Raw Value + Add Display Field (Simplest)
This approach keeps the original status field for writing (POST/PUT) and adds a separate read-only field for the human-readable display. It's straightforward and avoids any conversion headaches.
Update your serializer like this:
from rest_framework import serializers from .models import YourModel # Replace with your actual model name class YourModelSerializer(serializers.ModelSerializer): # Add a read-only field to return the display value status_display = serializers.SerializerMethodField() class Meta: model = YourModel # Include all original fields plus our new display field fields = list(serializers.ALL_FIELDS) + ['status_display'] # Or explicitly list fields if you prefer: # fields = ['id', 'status', 'status_display', ...your other fields...] def get_status_display(self, obj): # Use the model's built-in method to get the readable value return obj.get_status_display()
How this works:
- GET requests: You'll get both
status(raw value like 'to-do') andstatus_display(readable value like 'Work Not Yet Started') in the response. - POST/PUT requests: You only need to send the raw
statusvalue (e.g., 'to-do')—the serializer will handle it normally, no errors.
Option 2: Custom Field for Dual Behavior (Return Only Display Value)
If you want the API to return only the human-readable value in responses, but still accept either the raw value or the readable value in POST/PUT requests, create a custom field that handles both serialization and deserialization.
First, define the custom field:
from rest_framework import serializers class ChoiceDisplayField(serializers.Field): def __init__(self, choices, **kwargs): self.choice_map = dict(choices) # Make this field read-write by default super().__init__(**kwargs) def to_representation(self, value): # Convert raw database value to human-readable string return self.choice_map.get(value, value) def to_internal_value(self, data): # Convert incoming data (either raw or readable) to the database value for raw_value, display_value in self.choice_map.items(): if data == raw_value or data == display_value: return raw_value # If no match, throw a validation error raise serializers.ValidationError( f"Invalid status. Allowed values: {', '.join(self.choice_map.values())} or {', '.join(self.choice_map.keys())}" )
Then use it in your serializer:
class YourModelSerializer(serializers.ModelSerializer): # Replace the default status field with our custom one status = ChoiceDisplayField(choices=YourModel.STATUS_CHOICES) class Meta: model = YourModel fields = '__all__'
How this works:
- GET requests: The response will show the readable value (e.g., 'Work Not Yet Started') instead of the raw 'to-do'.
- POST/PUT requests: You can send either the raw value ('to-do') or the readable value ('Work Not Yet Started')—the field will convert it to the correct raw value for the database.
Both solutions work perfectly with your DRF 3.10.1 and Python 3.7.4 setup. Pick the one that fits your API's requirements best!
内容的提问来源于stack exchange,提问作者Raghwendra

