处理模型中的选项字段

作者: 思考的虫子 | 来源:发表于2018-12-31 06:29 被阅读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)
    

    方法二

    • 参考页面提到了更简单的方法,虽然我没有试过。
    • 即:继承 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]
    

    参考

    相关文章

      网友评论

        本文标题:处理模型中的选项字段

        本文链接:https://www.haomeiwen.com/subject/swfilqtx.html