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)
# 获取当前用户,并且隐藏了该字段,不会序列化返回给前端
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_加上要序列化的字段
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
注意
1.图片的url
# 在serializer的方法中使用Serializer的时候,他会检察上下文中有没有包含request,
# 如果有,那么在返回的图片url中会自动加上域名 http://....
# 如果没有,那么返回的url只会加上路径 /media/goods/images/......
2.参数 obj
def get_ad_goods(self, obj):
这里的obj是Serializer的Model经过查询后的结果集中的每个对象,也就是说,每查询到一个符合条件的纪录,该函数就会执行一次,向这个纪录中序列化这个函数字段
所以SerializerMethodField字段必须在继承ModelSerializer后使用,并且SerializerMethodField不能添加validate_user_email这样的验证方法
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)
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=True 这个值只返回给前端不让前端提交,前端只有读的权利
write_only=True 这个值让前端只会提交,不会再返回给前端,前端只有写的权利
验证是否唯一
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="已经收藏"
)
]
主要是让其通过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
必须返回一个实例
def update(self, instance, validated_data):
# 修改商品数量
instance.nums = validated_data['nums']
instance.save()
return instance
必须返回一个实例
tips:通常将serializer中的方法和Mixins中的方法结合起来看,因为Minix中会调用Serializer中的方法
init_data 指的是最原始的没有经过验证的数据
validated_data 指定是已经验证转换过的数据,比如nums已经验证无误并转换成了int类型
# 拿到user,nums,good
user = self.context['request'].user
nums = validated_data['nums']
goods = validated_data['goods'] # 这里取出的goods是Good对象
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()