DRF 处理模型中的选项字段

37次阅读

共计 2581 个字符,预计需要花费 7 分钟才能阅读完成。

GET API 返回选项的字符串而不是 ID
Django 中常常要用到选项字段
为了提高数据库效率和用户可读性,我们实际存储的是整数,显示的时候以字符串显示。这个属性在 Django Admin 得到了很好的处理,但到了 Django Rest Framework 就不会自动转化了。下面的在 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++ 参数的字段,FOO 是字段的名字,get_FOO_display() 返回选项的可读字符串
要实现 model 中的 Choice Field,在 GET 的时候显示选项名字,在 POST 的时候既能字符串又能接受 ID
方法一
比如模型中有个 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 这样的数字
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 这样的数字
def to_representation(self, obj):
“””Used while retrieving value for the field.”””
return self._choices[obj]
参考

正文完
 0