Serializer及ModelSerializer的使用

1、 使用Serializer类

serializer的基础使用

DRF中的serializer就类似于Djano中的Form,只不过Form是针对模板的,而serializer是针对json,而且非常强大
  • 以以下代码为例
class GoodsSerializer(serializers.ModelSerializer):
    # name = serializers.CharField(required=True,max_length=100)
    # click_num = serializers.IntegerField(default=0)
    # market_price = serializers.FloatField(default=0.0)
    # goods_cover = serializers.ImageField()
    # add_time = serializers.DateTimeField()

    class Meta:
        model = Goods
        # field = ('name','click_num','add_time') # 返回给前端的json中包含的字段
        field = "__all__" # 包含所有字段
        
     def create(self, validated_data):
        """
        Create and return a new `Good` instance, given the validated data.
        """
        return Goods.objects.create(**validated_data)
       

序列化单个字段

 name = serializers.CharField(required=True,max_length=100)

HiddleField隐藏字段

# 获取当前用户,并且隐藏了该字段,不会序列化返回给前端
user = serializers.HiddenField(
    default=serializers.CurrentUserDefault()
)

源码:提取request中的user,前端返回的token中会带上user信息

class CurrentUserDefault(object):
    def set_context(self, serializer_field):
        self.user = serializer_field.context['request'].user

    def __call__(self):
        return self.user

    def __repr__(self):
        return unicode_to_repr('%s()' % self.__class__.__name__)

自定义字段

  • 该方法的命名为get_加上要序列化的字段

```python
goods = serializers.SerializerMethodField()
#该方法的命名为get_加上要序列化的字段
def get_ad_goods(self, obj):
    print('get_ad_goods', obj.id)
    goods_json = {}
    # 这里传过来的只有'蔬菜水果','酒水饮料','粮油副食','生鲜食品'
    # 而他们的序号已经在IndexAd表中添加过了,所有会找到队友的商品纪录
    ad_goods = IndexAd.objects.filter(category_id=obj.id, )
    if ad_goods:
        good_ins = ad_goods[0].goods
        # 在serializer的方法中使用Serializer的时候,他会检察上下文中有没有包含request,
        # 如果有,那么在返回的图片url中会自动加上域名 http://....
        # 如果没有,那么返回的url只会加上路径 /media/goods/images/......
        goods_json = GoodsSerializer(good_ins, many=False, context={'request': self.context['request']}).data
    return goods_json

2、ModelSerializer

class Meta:
        model = Goods
        # field = ('name','click_num','add_time') # 返回给前端的json中包含的字段
        field = "__all__" # 包含所有字段
  • 重写create方法进行持久化操作
def create(self, validated_data):
        """
        Create and return a new `Good` instance, given the validated data.
        """
        return Goods.objects.create(**validated_data)
  • 直接使用外键主键作为值 PrimaryKeyRelatedField
goods = serializers.PrimaryKeyRelatedField(queryset=Goods.objects.all(),required=True)
	这里的外键与直接写一个类不同的是,只会将主键(goods_id)返回给前端,而不是返回goods中的所有字段。
  • 引入外键的序列化类,序列化外键表中的信息
class GoodCategorySerializer2(serializers.ModelSerializer):
    '''
    二级分类
    '''
    sub_cat = GoodCategorySerializer3(many=True)
    class Meta:
        model = GoodCategory
        fields = "__all__" # 包含所有字段

class GoodCategorySerializer(serializers.ModelSerializer):
    '''
    一级分类
    '''
    # sub_cat 是Category表中的自关联字段parent_category的relate_name,
    # 用于一对多反向引用时,点出二级分类,配置在一的那一方
    # 找出所有parent_category等于当前这个一级分类的parent_category的二级分类
    # many=True 表示会有多个
    sub_cat = GoodCategorySerializer2(many=True)
    class Meta:
        model = GoodCategory
        fields = "__all__" # 包含所有字段
  • many=True (一般用于反向查询)
class GoodsSerializer(serializers.ModelSerializer):
    category = GoodCategorySerializer()
    # images为GoodImage中外键的relate_name,用于反向查询
    images = GoodsImageSerializer(many=True) # 反向查询,一对多
  • many=False (一般用于正向查询)
# 这个表保存的是每个订单的每个商品数据
class OrderGoodsSerialzier(serializers.ModelSerializer):
    # 每个订单下的相同商品只会存在一条纪录
    # goods是OrderGoods中的外键
    goods = GoodsSerializer(many=False)
    class Meta:
        model = OrderGoods
        fields = "__all__"
  • 对应的OrderGoods
class OrderGoods(models.Model):
    """
    订单商品信息
    """
    order = models.ForeignKey(OrderInfo, verbose_name="订单信息",related_name="order_goods",on_delete=models.CASCADE)
    goods = models.ForeignKey(Goods, verbose_name="商品", on_delete=models.CASCADE)
    goods_sum = models.IntegerField(default=0, verbose_name="商品数量")

    add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")

注意:字段定义之后,如果也定义了meta类,则必须在类中的fields元祖中添加字段,或者写成fields = “all”

字段中的参数使用

以这块代码为例	
 '''
    负责用户注册时的初始化验证操作
    '''
    code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4,label="验证码",
                                 error_messages={
                                     "blank": "请输入验证码",
                                     "required": "请输入验证码",
                                     "max_length": "验证码格式错误",
                                     "min_length": "验证码格式错误"
                                 },
                                 help_text="验证码")
    username = serializers.CharField(label="用户名", help_text="用户名", required=True, allow_blank=False,
                                     validators=[UniqueValidator(queryset=User.objects.all(), message="用户已经存在")])

    password = serializers.CharField(
        style={'input_type': 'password'},help_text="密码", label="密码", write_only=True,
    )

    add_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M')

read_only和write_only

注意:

read_only=True 这个值只返回给前端不让前端提交,前端只有读的权利
write_only=True 这个值让前端只会提交,不会再返回给前端,前端只有写的权利

format 序列化输出的格式 validators 指定验证器

验证是否唯一
validators=[UniqueValidator(queryset=User.objects.all(), message="用户已经存在")

自定义字段验证规则

mobile = serializers.CharField(max_length=11)

def validate_mobile(self, mobile):
    '''
    验证手机号码
    :param mobile:
    :return:
    '''

    # 手机号是否注册
    if User.objects.filter(mobile=mobile).count():
        raise serializers.ValidationError("用户已经存在")

    # 验证手机号是否合法
    if not re.match(settings.REGEX_MOBILE,mobile):
        raise serializers.ValidationError('手机号码非法')

    # 验证验证码发送频率
    one_minute_ago = datetime.now() - timedelta(hours=0,minutes=1,seconds =0)
    if VerifyCode.objects.filter(add_time__gt=one_minute_ago,mobile=mobile).count():
        raise serializers.ValidationError('请超过60s后再次发送')

    return mobile

验证整体数据

def validate(self, attrs):
    attrs["mobile"] = attrs["username"]  # 整体验证
    del attrs["code"]   
    return attrs

联合唯一的验证

class Meta:
    model = UserFav
    validators = [
        UniqueTogetherValidator(
            queryset=UserFav.objects.all(),
            fields=('user', 'goods'),
            message="已经收藏"
        )
    ]

重写crete方法

例:
主要是让其通过CreateMixin中的验证,源码如下

serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)

重写Serilizer中的create

  # 这里不用ModelSerializer的原因是, 这里需要使用user, goods联合索引
    #
    # 我们当然可以使用联合索引来控制唯一性,但问题是,当我们重复添加商品时(只是希望他更新nums字段时(但是实际上是用update方法更新的)),
    # 第二条就会报错,因为会验证失败,所以我们不能再此使用联合索引,而需要使用更加灵活的Serializer并重写create方法
    #
    # 如果是ModelSerializer, 那么重复添加同一个商品时View中继承的CreateMinx中create方法中的验证就会报错 == serializer.is_valid(
    #     raise_exception=True) == 报错(因为是联合索引)
    #
    # 这样就不会再执行serializer.save()
    # 方法保存了,所有这里必须使用Serializer,重写create方法,而不使用联合索引
    #
    # 总结,使用联合索引时,如果需要重复创建商品,但实际上只更新某个字段,CreateMinx中的create方法验证不通过,必须使用Serializer,重写create方法
    def create(self, validated_data):
        # 拿到user,nums,good
        user = self.context['request'].user
        nums = validated_data['nums']
        goods = validated_data['goods']

        existed = ShoppingCart.objects.filter(user=user,goods=goods)
        if existed:
            existed = existed[0]
            # existed.objects.update(nums=(nums+1))  不可以,报错
            existed.nums += nums
            existed.save()
        else:
            existed = ShoppingCart.objects.create(**validated_data)

        return existed   #必须返回一个实例

重写update方法。通过Update中的验证

def update(self, instance, validated_data):
    # 修改商品数量
    instance.nums = validated_data['nums']
    instance.save()
    return instance  #必须返回一个实例
tips:通常将serializer中的方法和Mixins中的方法结合起来看,因为Minix中会调用Serializer中的方法

initial_data 和 validated_data

  • init_data 指的是最原始的没有经过验证的数据
  • validated_data 指定是已经验证转换过的数据,比如nums已经验证无误并转换成了int类型

在Serializer中拿到request对象

    # 拿到user,nums,good
        user = self.context['request'].user
        nums = validated_data['nums']
        goods = validated_data['goods'] # 这里取出的goods是Good对象

Serializer类与Mixin的执行顺序源码分析

这里以CreateMixin为例

CreateModelMixin

class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        print('createModel Mixin......')
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        print('perform_date')
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}

serializer.save()

   if self.instance is not None:
        self.instance = self.update(self.instance, validated_data)
        assert self.instance is not None, (
            '`update()` did not return an object instance.'
        )
    else:
        self.instance = self.create(validated_data)
        assert self.instance is not None, (
            '`create()` did not return an object instance.'
        )

打印的结果是:

createModel Mixin...... 首先执行CreateMixin
is_valid.......  执行BaseSerializer中的is_vaild
perform_date       执行CreateMixin的performdata
Base Serializer save......  执行BaseSerializer中的save
ModelSerializer create......... 执行ModelSerializer中的create
is_valid.......  在BrowsableAPIRenderer类中调用了serializer.is_valid
注意:最后一次的is_valid执行是与权限类permissions有关,在BrowsableAPIRenderer类中会执行
 def render_form_for_serializer(self, serializer):
        if hasattr(serializer, 'initial_data'):
            serializer.is_valid()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值