DRF 处理模型中的选项字段

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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值