前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python测试开发django-rest-framework-91.ChoiceField选项字段校验

python测试开发django-rest-framework-91.ChoiceField选项字段校验

作者头像
上海-悠悠
发布2021-02-03 10:04:27
1.4K0
发布2021-02-03 10:04:27
举报
文章被收录于专栏:从零开始学自动化测试

前言

当我们需要校验选项字段的时候,需用到 ChoiceField 来校验

选项

在 model 模型里面有个字段是选项字段, goods_status 可以有2种状态,0是下架,1是出售中,默认

代码语言:javascript
复制
class Goods(models.Model):
    """商品表"""

    goods_status = models.IntegerField(choices=(
                                                (0, '下架'),
                                                (1, '出售中')
                                               ),
                                       default=1,
                                       verbose_name="0下架 1出售中")

当我们查询的时候,goods_status 显示的是0 和 1

我们想让它显示 下架 和出售中,这样看起来更友好

序列化

在序列化类里面使用get_<字段名>_display的方法,该方法获得choice字段对应的数据 下架和出售中. 这里涉及到一个很有用的实例方法:get_<Field name>_display 对于模型中含有choices参数的字段, <Field name> 是字段的名字, get_FOO_display() 返回选项的可读字符串

代码语言:javascript
复制
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/

class GoodsSerializer(serializers.ModelSerializer):
    """序列化商品models"""
    create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
    update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)

    # chioce字段 get_<字段名>_display 显示名称
    goods_status = serializers.CharField(source='get_goods_status_display',
                                         required=False)
    # 必传字段
    goods_code = serializers.CharField(required=True,
                                       max_length=15,
                                       min_length=8,
                                       validators=[validators.UniqueValidator(queryset=Goods.objects.all(),
                                                                              message="goods_code 已存在")]
                                       )

    class Meta:
        model = Goods
        fields = '__all__'  # 返回全部的字段

序列化输出的时候,就可以显示出售中

当使用了source=’get_goods_status_display’ 后,这里goods_status字段就默认被设置为只读字段了,如果 post 要提交 create() 或者修改这个字段,就会报错

代码语言:javascript
复制
TypeError at /api/v1/goods/
Got a `TypeError` when calling `Goods.objects.create()`. 
This may be because you have a writable field on the serializer class that is not a valid argument to `Goods.objects.create()`.
You may need to make the field read-only, or override the GoodsSerializer.create() method to handle this correctly.
Original exception was:
 Traceback (most recent call last):
  File "E:\python36\lib\site-packages\rest_framework\serializers.py", line 932, in create
    instance = ModelClass._default_manager.create(**validated_data)
  File "E:\python36\lib\site-packages\django\db\models\manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "E:\python36\lib\site-packages\django\db\models\query.py", line 415, in create
    obj = self.model(**kwargs)
  File "E:\python36\lib\site-packages\django\db\models\base.py", line 495, in __init__
    raise TypeError("'%s' is an invalid keyword argument for this function" % kwarg)
TypeError: 'get_goods_status_display' is an invalid keyword argument for this function

也可以单独写一个读取choice字段的方法, get_<字段名称>自定义输出内容

代码语言:javascript
复制
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/

class GoodsSerializer(serializers.ModelSerializer):
    """序列化商品models"""
    create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
    update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)

    # 设置SerializerMethodField
    goods_status = serializers.SerializerMethodField(read_only=False, write_only=False)

    def get_goods_status(self,obj):
        """get_<字段名称> 重写goods_status"""
        return obj.get_goods_status_display()

    class Meta:
        model = Goods
        fields = '__all__'  # 返回全部的字段

这样写在提交的时候带上goods_status不会报错了,但不会存到数据库中(相当于忽略这个字段的校验了),达不到我们的期望结果。

to_representation 使用

接下来我们希望提交数据的时候,还是用原来的数字0和1提交,读出来的时候显示对应的名称 重写 ModelSerializer 类里面的 to_representation 方法,自定义序列化数据的返回,此时需去掉上面的

代码语言:javascript
复制
# chioce字段 get_<字段名>_display 显示名称
    goods_status = serializers.CharField(source='get_goods_status_display',
                                         required=False)

重写 ModelSerializer 类里面的 to_representation 方法

代码语言:javascript
复制
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/

class GoodsSerializer(serializers.ModelSerializer):
    """序列化商品models"""
    create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
    update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)

    # 必传字段
    goods_code = serializers.CharField(required=True,
                                       max_length=15,
                                       min_length=8,
                                       validators=[validators.UniqueValidator(queryset=Goods.objects.all(),
                                                                              message="goods_code 已存在")]
                                       )
    def to_representation(self, instance):
        """to_representation自定义序列化数据的返回"""
        data = super().to_representation(instance)
        data.update(goods_status=instance.get_goods_status_display())
        return data

    class Meta:
        model = Goods
        fields = '__all__'  # 返回全部的字段

此时传状态对应的数字,返回查询的结果就是显示名称

ChoiceField 选项字段

ChoiceField 专门用来处理有choices选项的问题,处理起来更高级一点,比如数据库里面有多种状态,但是状态2不希望用户去操作,只让用户添加0和1两种状态

代码语言:javascript
复制
    goods_status = models.IntegerField(choices=(
                                                (0, '下架'),
                                                (1, '出售中'),
                                                (2, '黑名单')
                                               ),
                                       default=1,
                                       verbose_name="0下架 1出售中")

于是可以用到ChoiceField, 必须传choices 参数选项

代码语言:javascript
复制
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/

class GoodsSerializer(serializers.ModelSerializer):
    """序列化商品models"""
    create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
    update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
    # get_<字段名>_display

    goods_status = serializers.ChoiceField(choices=(
                                                (0, '下架'),
                                                (1, '出售中')
                                               ),
                                           required=False)

    def to_representation(self, instance):
        """to_representation自定义序列化数据的返回"""
        data = super().to_representation(instance)
        data.update(goods_status=instance.get_goods_status_display())
        return data

    class Meta:
        model = Goods
        fields = '__all__'  # 返回全部的字段

实现效果跟上面的一样,功能多了一个限制,只能传数字 0 和 1 两种状态,返回的时候显示状态名称

重写 ChoiceField

如果我们在添加的时候,既可以添加 0 和 1 这 2 个状态,也可以提交”下架” 和 “出售中” 这2种名称,查询的时候显示名称。 对提交的数据,反序列化处理,需重写 ChoiceField 方法

代码语言:javascript
复制
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/

class ChoiceField(serializers.ChoiceField):
    """重写ChoiceField"""

    def to_representation(self, obj):
        """返回状态名称"""
        if obj == '' and self.allow_blank:
            return obj
        return self._choices[obj]

    def to_internal_value(self, data):
        """支持choice的key 或value名称的写入"""
        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())))

class GoodsSerializer(serializers.ModelSerializer):
    """序列化商品models"""
    create_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)
    update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', required=False)

    # 直接用上面重写的ChoiceField
    goods_status = ChoiceField(choices=(
                                        (0, '下架'),
                                        (1, '出售中')
                                       ),
                                required=False)
    # 必传字段
    goods_code = serializers.CharField(required=True,
                                       max_length=15,
                                       min_length=8,
                                       validators=[validators.UniqueValidator(queryset=Goods.objects.all(),
                                                                              message="goods_code 已存在")]
                                       )
    goods_stock = serializers.IntegerField(required=True,
                                           min_value=1,
                                           max_value=10000)

    class Meta:
        model = Goods
        fields = '__all__'  # 返回全部的字段

传状态名称可以支持

传数字也支持

如果只想接收用户传状态名称,可以重写 ChoiceField 的 to_internal_value 方法

代码语言:javascript
复制
    def to_internal_value(self, data):
        """支持choice的value名称的写入"""

        if data == '' and self.allow_blank:
            return ''

        for key, val in self._choices.items():
            if val == data:
                return key
        self.fail('invalid_choice', input=data)

关于choicefield 相关的用法可以参考https://stackoverflow.com/questions/28945327/django-rest-framework-with-choicefield

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 从零开始学自动化测试 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 选项
  • 序列化
  • to_representation 使用
  • ChoiceField 选项字段
  • 重写 ChoiceField
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档