GET API 返回選項的字符串而不是ID
Django 中常常要用到選項字段
只讀顯示字符串
為了提高數(shù)據(jù)庫效率和用戶可讀性,我們實際存儲的是整數(shù),顯示的時候以字符串顯示。
這個屬性在 Django Admin 得到了很好的處理,但到了 Django Rest Framework 就不會自動轉(zhuǎn)化了墙懂。
下面的在GET的時候顯示字符串,但這個字段就變成只讀但了扮念。
# 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 字段损搬,該字段變成只讀的了。
gender = serializers.CharField(source='get_gender_display')
class Meta:
model = User
# viewsets.py
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
這里涉及到一個有趣的實例方法: get_FOO_display
對于模型中含有 ++choices++ 參數(shù)的字段柜与, FOO 是字段的名字巧勤, get_FOO_display() 返回選項的可讀字符串
【重點】可讀寫:GET-顯示選項名字 POST-接受數(shù)字
要實現(xiàn) model 中的 Choice Field, 在 GET 的時候顯示選項名字弄匕,在POST的時候既能字符串又能接受ID
接受下面singo評論的建議:參考 stackoverflow 最佳方法如下:
class CommonInfoSerializer(serializers.ModelSerializer):
....
def to_representation(self, instance):
data = super().to_representation(instance)
# 返回 data 是一個字典颅悉,更新里面的 status 值
data.update(status=instance.get_status_display())
return data
get_<field_name>_display()
是一個自帶的方法,返回選項字段的可讀值(字符串)
完美解決 ??
以下其他方法在更復雜的情況下才考慮迁匠,可以忽略
方法一
比如模型中有個status
# models.py
class CommonInfo(models.Model):
INACTIVE = 0
PUBLISHED = 1
PENDING = -1
DRAFT = -2
REPORTED = -3
DELETED = -4
STATUS_CHOICES = (
(INACTIVE, 'INACTIVE'),
(PUBLISHED, 'PUBLISHED'),
(PENDING, 'PENDING'),
(DRAFT, 'DRAFT'),
(REPORTED, 'REPORTED'),
(DELETED, 'DELETED'),
)
status = models.SmallIntegerField(
choices=STATUS_CHOICES, default=PUBLISHED)
# utils.py
from rest_framework import serializers
from collections import OrderedDict
class ChoiceDisplayField(serializers.Field):
"""Custom ChoiceField serializer field."""
def __init__(self, choices, **kwargs):
"""init."""
self._choices = OrderedDict(choices)
super(ChoiceDisplayField, self).__init__(**kwargs)
# 返回可讀性良好的字符串而不是 1剩瓶,-1 這樣的數(shù)字
def to_representation(self, obj):
"""Used while retrieving value for the field."""
return self._choices[obj]
def to_internal_value(self, data):
"""Used while storing value for the field."""
for i in self._choices:
# 這樣無論用戶POST上來但是CHOICES的 Key 還是Value 都能被接受
if i == data or self._choices[i] == data:
return i
raise serializers.ValidationError("Acceptable values are {0}.".format(list(self._choices.values())))
# serializers.py
from utils import ChoiceDisplayField
class CommonInfoSerializer(serializers.ModelSerializer):
INACTIVE = 0
PUBLISHED = 1
PENDING = -1
DRAFT = -2
REPORTED = -3
DELETED = -4
STATUS_CHOICES = (
(INACTIVE, 'INACTIVE'),
(PUBLISHED, 'PUBLISHED'),
(PENDING, 'PENDING'),
(DRAFT, 'DRAFT'),
(REPORTED, 'REPORTED'),
(DELETED, 'DELETED'),
)
status = ChoiceDisplayField(choices=STATUS_CHOICES)
方法二
- 參考頁面提到了更簡單的方法,雖然我沒有試過城丧。
- 即:繼承 ChoiceField 而不是 Field延曙,就樣就不用寫
to_internal_value()
了。 - 但 POST 只能接受 ID亡哄, 不能接受字符串枝缔,如下
from rest_framework import serializers
from collections import OrderedDict
class ChoiceDisplayField(serializers.ChoiceField):
"""Custom ChoiceField serializer field."""
def __init__(self, choices, **kwargs):
"""init."""
self._choices = OrderedDict(choices)
super(ChoiceDisplayField, self).__init__(**kwargs)
# 返回可讀性良好的字符串而不是 1,-1 這樣的數(shù)字
def to_representation(self, obj):
"""Used while retrieving value for the field."""
return self._choices[obj]