五十五、序列化组件

一 序列化组件介绍

是drf提供的一个类,from rest_framework import serializers,继承它后写自己的类,可以序列化queryset或者单个对象。

二 序列化组件基本使用

models.py

from django.db import models

# 创建员工表
class Employee(models.Model):
    name = models.CharField(max_length=32)
    department = models.CharField(max_length=32)
    phone = models.CharField(max_length=32)

创建一个py文件用于写序列化组件类,这里叫emp_serializer.py

from rest_framework import serializers
from app01.models import Employee


class SerializersEmp(serializers.Serializer):
    name = serializers.CharField()
    department = serializers.CharField()
    phone = serializers.CharField()

views.py

from app01.emp_serializer import SerializersEmp


# 通过序列化组件
class EmployeeView(APIView):
	# 获取所有员工信息
    def get(self, request):
        query_set = Employee.objects.all()
        # 序列化多个需要指定参数many=True
        ser = SerializersEmp(instance=query_set, many=True)
        return Response(ser.data)

    def post(self, request):
    	# 新增员工数据
    	# 将数据反序列化
        ser = SerializersEmp(data=request.data)
        if ser.is_valid():  # 判断数据是否合法
            ser.save()  # 调用save方法 没有instance参数就是创建
            # 需要在序列化组件中重写create方法
            return Response(ser.data)
        return Response(ser.errors)


class EmployeeViewDetail(APIView):
    # 获取单个
    def get(self, request, pk):
        emp_obj = Employee.objects.filter(pk=pk).first()
        ser = SerializersEmp(instance=emp_obj)
        return Response(ser.data)
        
	# 修改
    def put(self, request, pk):
        emp_obj = Employee.objects.filter(pk=pk).first()
        ser = SerializersEmp(instance=emp_obj, data=request.data)
        if ser.is_valid():
            ser.save()  # 用instance参数是修改,需要重写序列化组件中的update方法
            return Response(ser.data)
        return Response(ser.errors)
        
	# 修改
    def patch(self, request, pk):
        emp_obj = Employee.objects.filter(pk=pk).first()
        ser = SerializersEmp(instance=emp_obj, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        return Response(ser.errors)

    def delete(self, request, pk):
        Employee.objects.filter(pk=pk).delete()
        return Response()  # 返回空

urls.py

from app01.views import EmployeeView, EmployeeViewDetail
urlpatterns = [
    path('employee/', EmployeeView.as_view()),
    path('employee/<int:pk>/', EmployeeViewDetail.as_view()),
]

SerializersEmp类中的create方法

def create(self, validated_data):
	# 将校验过的数据写入表中
    Employee.objects.create(**validated_data)
    # 返回写入的对象
    return validated_data

SerializersEmp类中的update方法

def update(self, instance, validated_data):
    instance.name = validated_data.get('name')
    instance.department = validated_data.get('department')
    instance.phone = validated_data.get('phone')
    instance.save()
    return instance

练习

fbv写—》写个装饰器,装饰在视图函数,只要一装饰,以后的request就可以使用request.data,这个data无论是那种编码格式,都有数据。

def decorate(func_name):
    def inner(request, *args, **kwargs):
        if request.content_type == 'multipart/form-data':
            request.data = request.POST
        if request.content_type == 'application/x-www-form-urlencoded':
            request.data = request.POST
        if request.content_type == 'application/json':
            import json
            request.data = json.loads(request.body)
        res = func_name(request, *args, **kwargs)
        return res

    return inner


@decorate
def homework(request):
    # application/json  multipart/form-data
    # application/x-www-form-urlencoded
    print(request.data)
    return HttpResponse('ok')

路由

path('homework/', views.homework),

三 序列化类常用字段类和字段参数

3.1 常用字段类

# BooleanField	BooleanField()
# NullBooleanField	NullBooleanField()
# CharField	CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
# EmailField	EmailField(max_length=None, min_length=None, allow_blank=False)
# RegexField	RegexField(regex, max_length=None, min_length=None, allow_blank=False)
# SlugField	SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
# URLField	URLField(max_length=200, min_length=None, allow_blank=False)
# UUIDField	UUIDField(format=’hex_verbose’) format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
# IPAddressField	IPAddressField(protocol=’both’, unpack_ipv4=False, **options)
# IntegerField	IntegerField(max_value=None, min_value=None)
# FloatField	FloatField(max_value=None, min_value=None)
# DecimalField	DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
# DateTimeField	DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
# DateField	DateField(format=api_settings.DATE_FORMAT, input_formats=None)
# TimeField	TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
# DurationField	DurationField()
# ChoiceField	ChoiceField(choices) choices与Django的用法相同
# MultipleChoiceField	MultipleChoiceField(choices)
# FileField	FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
# ImageField	ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)

# 常用
# CharField
# BooleanField
# IntegerField
# DecimalField

# ListField
# DictField

3.2 常用字段参数

选项参数:

参数名称	                        作用

# 给CharField字段类使用的参数
max_length	                   最大长度
min_lenght	                   最小长度
allow_blank	                   是否允许为空
trim_whitespace	           是否截断空白字符

# 给IntegerField、DecimalField字段类使用的参数
max_value	                   最小值
min_value	                   最大值

通用参数:

参数名称 	                  说明
required	             表明该字段在反序列化时必须输入,默认True
default	                 反序列化时使用的默认值
allow_null	             表明该字段是否允许传入None,默认False
validators	             该字段使用的验证器
error_messages	         包含错误编号与错误信息的字典
label	                 用于HTML展示API页面时,显示的字段名称
help_text	             用于HTML展示API页面时,显示的字段帮助提示信息


#  重点
read_only	             表明该字段仅用于序列化输出,默认False
write_only	             表明该字段仅用于反序列化输入,默认False

四 序列化高级用法source

-source指定的可以是字段,也可以是方法,用于重命名
-source可以做跨表查询
class BookSerializer(serializers.Serializer):
book_name = serializers.CharField(max_length=8, min_length=3,source='name')

publish_name = serializers.CharField(max_length=8, min_length=3,source='publish.name')

# xx表示模型类中的方法 会自动加括号执行
xx = serializers.CharField(max_length=8, min_length=3,source='xx') 

五 定制序列化字段的两种方式

5.1 在序列化类中写SerializerMethodField

xxx = serializers.SerializerMethodField()

# obj是当前序列化类的对象
def get_xxx(self, obj):
    return {'name': obj.name}


# 获取书籍对象的所有作者
author = serializers.SerializerMethodField()
def get_author(self, book_obj):
	return [{'name': obj.name, 'age': obj.age, 'phone': obj.author_detail.phone} for obj in book_obj.author.all()]

5.2 在模型类中定义方法

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    create_time = models.DateField()
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    author = models.ManyToManyField(to='Author')

    def __str__(self):
        return f'{self.name}'

    # 可以加property装饰 也可以不加 序列化组件可以不用加括号拿到
    @property
    def publish_detail(self):
        return {'name': self.publish.name, 'city': self.publish.city, 'email': self.publish.email}

    def author_list(self):
        return [{'name': obj.name, 'age': obj.age, 'phone': obj.author_detail.phone} for obj in self.author.all()]

5.3 有关联关系表数据创建

# 只写不读 只反序列化不序列化
publish = serializers.IntegerField(write_only=True)
author = serializers.ListField(write_only=True)

# 只读不写 只序列化不反向序列化
publish_detail = serializers.DictField(read_only=True)
author_list = serializers.ListField(read_only=True)

# 继承serializers.Serializer一定要重写creata方法和update方法
def create(self, validated_data):
    name = validated_data.get('name')
    price = validated_data.get('price')
    create_time = validated_data.get('create_time')
    publish = validated_data.get('publish')
    author = validated_data.get('author')
    # 创建书籍数据
    obj = Book.objects.create(name=name, price=price, create_time=create_time, publish_id=publish)
    # 添加书籍和作者关系 多对多
    obj.author.add(*author)
    return obj


def update(self, instance, validated_data):
    name = validated_data.get('name')
    price = validated_data.get('price')
    create_time = validated_data.get('create_time')
    publish = validated_data.get('publish')
    author = validated_data.get('author')
    instance.name = name
    instance.price = price
    instance.create_time = create_time
    instance.publish_id = publish
    instance.author.set(author)
    instance.save()
    return instance

总结:继承Serializer的序列化组件需要写校验的每个字段,创建和修改数据时,需要重写create和update方法。而使用ModelSerializer,不需要写这些。

六 反序列化之数据校验

首先是校验字段自己的校验规则,通过后校验局部钩子,最后校验全局钩子。

6.1 字段自己的校验规则

class BookSerializer(serializers.Serializer):
	# 继承Serializer字段自己的校验规则
    book_name = serializers.CharField(max_length=8, min_length=3, required=True,)
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['name', 'price', 'create_time', 'publish', 'author', 'publish_detail', 'author_list']
        extra_kwargs = {
        	#继承ModelSerializer字段自己的校验规则
            'name': {'max_length': 8, 'min_length': 3},
            }

6.2 局部钩子

def validate_name(self, name):
    if 'sb' in name:
        raise ValidationError('名字不能包含敏感词')
    return name


def validate_price(self, price):
    if price == 66:
        raise ValidationError('价格不能是66')
    return price

6.3 全局钩子

def validate(self,attrs):
	if attrs.get('password') != attrs.get('re_password'):
		raise ValidationError('两次密码不一致')
	return attrs

七 模型类序列化器(ModelSerializer)的使用

步骤:

  1. 定义一个序列化组件类,继承ModelSerializer。
  2. 在序列化组件类中定义一个Meta类,类中指定model和fields参数。
  3. 在Meta类中指定extra_kwargs,给字段添加参数。
  4. 在序列化类中,可以重写某个字段,优先使用你重写的。
  5. 不需要再重写create和update方法。
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        # fields = ['__all__'] 所有字段都序列化
        fields = ['name', 'price', 'create_time', 'publish', 'author', 'publish_detail', 'author_list']
        extra_kwargs = {
            'name': {'max_length': 8, 'min_length': 3},
            'price': {'min_value': 9, 'max_value': 199},
            'publish': {'write_only': True},
            'author': {'write_only': True},
            'publish_detail': {'read_only': True},
            'author_list': {'read_only': True},
        }

    def validate_name(self, name):
        if 'sb' in name:
            raise ValidationError('名字不能包含敏感词')
        return name

    def validate_price(self, price):
        if price == 66:
            raise ValidationError('价格不能是66')
        return price

八 反序列化数据校验源码分析

入口是序列化组件对象.is_valid()方法。

BaseSerializer里的is_valid

def is_valid(self, *, raise_exception=False):
		
    if not hasattr(self, '_validated_data'):
        try:
        	# 真正开始校验 如果校验成功返回校验通过的数据
            self._validated_data = self.run_validation(self.initial_data)
        except ValidationError as exc:
            self._validated_data = {}
            self._errors = exc.detail
        else:
            self._errors = {}

    if self._errors and raise_exception:
        raise ValidationError(self.errors)

    return not bool(self._errors)

执行self.run_validation(self.initial_data)
Serializer里的run_validation

def run_validation(self, data=empty):
    # 执行局部钩子
    value = self.to_internal_value(data)
    try:
    	# 执行全局钩子
        self.run_validators(value)
        value = self.validate(value)
        assert value is not None, '.validate() should return the validated data'
    except (ValidationError, DjangoValidationError) as exc:
        raise ValidationError(detail=as_serializer_error(exc))

    return value
    def to_internal_value(self, data):=
        for field in fields:
        	# 反射判断是否有局部钩子函数
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            primitive_value = field.get_value(data)
            try:
            	# 字段本身的校验规则
                validated_value = field.run_validation(primitive_value)
                # 局部钩子校验
                if validate_method is not None:
                    validated_value = validate_method(validated_value)
            except ValidationError as exc:
                errors[field.field_name] = exc.detail
            except DjangoValidationError as exc:
                errors[field.field_name] = get_error_detail(exc)
            except SkipField:
                pass
            else:
                set_value(ret, field.source_attrs, validated_value)

        if errors:
            raise ValidationError(errors)

        return ret

练习

写出book表(带关联关系)5 个接口

# urls.py
from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookView.as_view()),
    path('books/<int:pk>/', views.BookViewV2.as_view()),
]


# models.py
from django.db import models


# Create your models here.

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    create_time = models.DateField()
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    author = models.ManyToManyField(to='Author')

    def __str__(self):
        return f'{self.name}'

    # 可以加property装饰 也可以不加 序列化组件可以不用加括号拿到
    @property
    def publish_detail(self):
        return {'name': self.publish.name, 'city': self.publish.city, 'email': self.publish.email}

    def author_list(self):
        return [{'name': obj.name, 'age': obj.age, 'phone': obj.author_detail.phone} for obj in self.author.all()]


class Publish(models.Model):
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()

    def book_list(self):
        return [{'name': obj.name, 'price': obj.price} for obj in self.book_set.all()]  # 反向查询结果有多个表名小写加_set

    def __str__(self):
        return f'{self.name}'


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)

    def __str__(self):
        return f'{self.name}'

    @property
    def detail(self):
        return {'phone': self.author_detail.phone, 'birthday': self.author_detail.birthday,
                'addr': self.author_detail.addr}


class AuthorDetail(models.Model):
    phone = models.CharField(max_length=32)
    birthday = models.DateField()
    addr = models.CharField(max_length=32)

    def aut(self):
        return {'name': self.author.name, 'age': self.author.age}


# serializer.py
from rest_framework import serializers
from .models import *
from rest_framework.exceptions import ValidationError


# ModelSerializer(简单,不用重写create和update)
# name最大8,最小3,名字中不能带sb
# price最小9,最大199,不能为66

# 基于ModelSerializer
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['name', 'price', 'create_time', 'publish', 'author', 'publish_detail', 'author_list']
        extra_kwargs = {
            'name': {'max_length': 8, 'min_length': 3},
            'price': {'min_value': 9, 'max_value': 199},
            'publish': {'write_only': True},
            'author': {'write_only': True},
            'publish_detail': {'read_only': True},
            'author_list': {'read_only': True},
        }

    def validate_name(self, name):
        if 'sb' in name:
            raise ValidationError('名字不能包含敏感词')
        return name

    def validate_price(self, price):
        if price == 66:
            raise ValidationError('价格不能是66')
        return price


# 基于Serializer
# name最大8,最小3,名字中不能带sb
# price最小9,最大199,不能为66
class BookSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=8, min_length=3, required=True,
                                 error_messages={
                                     'max_length': '名字最长8位',
                                     'min_length': '名字最短3位',
                                     'required': '名字必填'
                                 })
    price = serializers.DecimalField(max_value=199, min_value=9, required=True, max_digits=6, decimal_places=2,
                                     error_messages={
                                         'max_value': '最大价格不能超过199',
                                         'min_value': '价格不能小于9'
                                     })
    # xxx = serializers.SerializerMethodField()
    #
    # def get_xxx(self, obj):
    #     return {'name': obj.name}

    create_time = serializers.DateField()
    publish = serializers.IntegerField(write_only=True)
    author = serializers.ListField(write_only=True)
    publish_detail = serializers.DictField(read_only=True)
    author_list = serializers.ListField(read_only=True)

    def validate_name(self, name):
        if 'sb' in name:
            raise ValidationError('名字不能包含敏感词')
        return name

    def validate_price(self, price):
        if price == 66:
            raise ValidationError('价格不能是66')
        return price

    def create(self, validated_data):
        name = validated_data.get('name')
        price = validated_data.get('price')
        create_time = validated_data.get('create_time')
        publish = validated_data.get('publish')
        author = validated_data.get('author')
        # 创建书籍数据
        obj = Book.objects.create(name=name, price=price, create_time=create_time, publish_id=publish)
        # 添加书籍和作者关系 多对多
        obj.author.add(*author)
        return obj

    def update(self, instance, validated_data):
        name = validated_data.get('name')
        price = validated_data.get('price')
        create_time = validated_data.get('create_time')
        publish = validated_data.get('publish')
        author = validated_data.get('author')
        instance.name = name
        instance.price = price
        instance.create_time = create_time
        instance.publish_id = publish
        instance.author.set(author)
        instance.save()
        return instance


# views.py
from rest_framework.views import APIView
from .models import *
from rest_framework.response import Response
from .serializer import BookModelSerializer
from .serializer import PublishModelSerializer, AuthorModelSerializer, DetailModelSerializer

# 基于ModelSerializer进行序列化和反序列化
# class BookView(APIView):
#     # 获取全部
#     def get(self, request):
#         query_set = Book.objects.all()
#         ser = BookModelSerializer(instance=query_set, many=True)
#         return Response(ser.data)
#
#     # 新增一条
#     def post(self, request):
#         # 反序列化
#         ser = BookModelSerializer(data=request.data)
#         if ser.is_valid():
#             # 使用ModelSerializer不用重写create方法
#             ser.save()
#             return Response({'code': 100, 'msg': '图书新增成功'})
#         return Response(ser.errors)
#
#
# class BookViewV2(APIView):
#     def get(self, request, pk):
#         obj = Book.objects.filter(pk=pk).first()
#         ser = BookModelSerializer(instance=obj)
#         return Response(ser.data)
#
#     def put(self, request, pk):
#         obj = Book.objects.filter(pk=pk).first()
#         ser = BookModelSerializer(instance=obj, data=request.data)
#         if ser.is_valid():
#             # 不用再重写update方法
#             ser.save()
#             return Response(ser.data)
#         return Response(ser.errors)
#
#     def delete(self, request, pk):
#         Book.objects.filter(pk=pk).delete()
#         return Response({})


# 基于Serializer序列化组件进行序列化和反序列化
from .serializer import BookSerializer


class BookView(APIView):
    def get(self, request):
        query_set = Book.objects.all()
        ser = BookSerializer(instance=query_set, many=True)
        return Response(ser.data)

    def post(self, request):
        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            # 需要重写序列化组件的create方法
            ser.save()
            return Response(ser.data)
        return Response(ser.errors)


class BookViewV2(APIView):
    def get(self, request, pk):
        obj = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=obj)
        return Response(ser.data)

    def put(self, request, pk):
        obj = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=obj, data=request.data)
        if ser.is_valid():
            # 需要重写序列化组件的update方法
            ser.save()
            return Response(ser.data)
        return Response(ser.errors)

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return Response({})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值