Django + Django Rest Framework 实践django-rest-frameworkdjango-rest-framework开发

处理模型中的选项字段

2018-12-31  本文已影响4人  思考的虫子

处理模型中的选项字段

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)

方法二

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]

参考

上一篇下一篇

猜你喜欢

热点阅读