Django DRF 序列化类

1. 序列化类 Serializer

1. 序列化
   序列化器会把模型对象转换成字典,经过response以后变成json字符串
2. 反序列化
   把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
   反序列化也可以进行数据校验,例如数据的长度等等

不使用 Serializer 序列化

  1. 模型层
from django.db import models

class MyBook(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()

  1. 创建视图函数
class BookView(View):
    # 查询全部记录,QuerySet对象不能被JsonResponse序列化
    def get(self, request):
        books = []
        book_queryset = models.MyBook.objects.all()
        for book in book_queryset:
            books.append({'id': book.id, 'title': book.title, 'price': book.price})
        # safe=False作用于非字典类型,json_dumps_params参数将 'ensure_ascii': False 传入 json.dumps 显示中文
        return JsonResponse(books, safe=False, json_dumps_params={'ensure_ascii': False})

    # 新增记录
    def post(self, request):
        title = request.POST.get('title')
        price = request.POST.get('price')
        book = models.MyBook.objects.create(title=title, price=price)
        # return JsonResponse({'id':book.id,'name':book.title,'price':book.price})
        return JsonResponse({'msg': '新增成功', 'code': 100})


class BookDetailView(View):
    # 获取指定记录
    def get(self, request, pk):
        book = models.MyBook.objects.filter(pk=pk).first()
        return JsonResponse({'id': book.id, 'title': book.title, 'pirce': book.price})

    # 删除指定记录
    def delete(self, request, pk):
        models.MyBook.objects.all().filter(pk=pk).delete()
        return JsonResponse({'code': 100, 'msg': "删除成功"})

    # 编辑指定记录
    def put(self, request, pk):
        # 获取前端传入的值,但是put请求的值不能从request.POST中取出
        import json
        try:
            data = json.loads(request.body)
        except Exception as e:
            data = request.POST
        request.data = data
        book = models.MyBook.objects.all().filter(pk=pk).first()
        book.price = request.data.get('price')
        book.title = request.data.get('title')
        book.save()

        return JsonResponse({'code': 100, 'msg': "修改成功"})

  1. 路由
urlpatterns = [
    path('books/', views.BookView.as_view()),
    path('books/<int:pk>', views.BookDetailView.as_view()),
]

不使用 Serializer 序列化会比较繁琐。

1.1 Serializer 基本使用

models.py
需要先建立一个模型类

from django.db import models

class MyBook(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()

urls.py

urlpatterns = [
    path('myApi/', views.MyAPI.as_view()),
]

serializer
在应用下新建任意名字文件例如 serializer.py 文件,在文件中导入刚才建立的表并导入 serializers,根据表中字段创建序列化类,序列化的字段可以自定义

from rest_framework import serializers
from .models import MyBook

class BookSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    price = serializers.IntegerField()

    def create(self, validated_data):
        return MyBook.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """更新,instance为要更新的对象实例"""
        instance.title = validated_data.get('title', instance.title)
        instance.price = validated_data.get('price', instance.price)
        instance.save()
        return instance


views.py
创建好序列化类后导入到视图函数中使用。

from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import models
from .serializer import BookSerializer


class BookDetailView(APIView):
    def get(self, request, pk):
        book_obj = models.MyBook.objects.filter(pk=pk).first()
        res = BookSerializer(instance=book_obj)
        return Response(res.data)

    def delete(self, request, pk):
        models.MyBook.objects.filter(pk=pk).delete()
        return Response()

    def put(self, request, pk):
        book_obj = models.MyBook.objects.filter(pk=pk).first()
        data = request.data
        res = BookSerializer(instance=book_obj, data=data)
        if res.is_valid():
            res.save()
            return Response(res.data)
        return Response({"msg": "未通过校验"})


class BookView(APIView):
    def get(self, request):
        book_obj = models.MyBook.objects.all()
        res = BookSerializer(instance=book_obj, many=True)
        return Response(res.data)

    def post(self, request):
        data = request.data
        res = BookSerializer(data=data)
        if res.is_valid():
            res.save()
        return Response(res.data)

1.2 定义序列化类

在基础使用可以看出,定义序列化类需要先创建模型表,如下简易表

from django.db import models

class MyBook(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()

Django REST framework中 的 Serializer使用类来定义须继承 rest_framework.serializers.Serializer。

序列化类字段需和模型表中字段对应,但是 serializer 并非只能为数据库模型类定义,也可以为非数据库模型类的数据定义。serializer是独立于数据库之外的存在。定义序列化字段需要使用类似于定义模型表的方式,如下示例

from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    price = serializers.IntegerField()

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

  1. 常用字段类型:
字段字段构造方式
BooleanFieldBooleanField()
NullBooleanFieldNullBooleanField()
CharFieldCharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailFieldEmailField(max_length=None, min_length=None, allow_blank=False)
RegexFieldRegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugFieldSlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
URLFieldURLField(max_length=200, min_length=None, allow_blank=False)
UUIDFieldUUIDField(format=’hex_verbose’) format: 1)
IPAddressFieldIPAddressField(protocol=’both’, unpack_ipv4=False, **options)
IntegerFieldIntegerField(max_value=None, min_value=None)
FloatFieldFloatField(max_value=None, min_value=None)
DecimalFieldDecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
DateTimeFieldDateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateFieldDateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeFieldTimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationFieldDurationField()
ChoiceFieldChoiceField(choices) choices与Django的用法相同
MultipleChoiceFieldMultipleChoiceField(choices)
FileFieldFileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageFieldImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListFieldListField(child=, min_length=None, max_length=None)
DictFieldDictField(child=)
  1. 选项参数:
参数名称作用
max_length最大长度
min_lenght最小长度
allow_blank是否允许为空
trim_whitespace是否截断空白字符
max_value最大值
min_value最小值
  1. 通用参数:
参数名称说明
read_only表明该字段仅用于序列化输出,默认False
write_only表明该字段仅用于反序列化输入,默认False
required表明该字段在反序列化时必须输入,默认True
default反序列化时使用的默认值
allow_null表明该字段是否允许传入None,默认False
validators该字段使用的验证器
error_messages包含错误编号与错误信息的字典
label用于HTML展示API页面时,显示的字段名称
help_text用于HTML展示API页面时,显示的字段帮助提示信息

1.3 创建 Serializer 对象

定义好Serializer类后,就可以创建Serializer对象了。

Serializer 的构造方法为:

Serializer(instance=None, data=empty, **kwarg)

说明:

1)用于序列化时,将模型类对象传入instance参数
2)用于反序列化时,将要被反序列化的数据传入data参数
3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据例如
   serializer = AccountSerializer(account, context={'request': request})

注意点:

  • 使用序列化器的时候需要注意,声明序列化器后,不会自动执行,需要在视图中调用才可以。
  • 序列化器无法直接接收数据,需要我们在视图中创建序列化器对象时把使用的数据传递过来。
  • 序列化器的字段声明类似于表单系统。
  • 开发 restful api 时,序列化器会帮我们把模型数据转换成字典.
  • drf 提供的视图会帮我们把字典转换成 json,或者把客户端发送过来的数据转换字典.

1.4 序列化

在服务器响应时,使用序列化器可以完成对数据的序列化。
序列化器会把模型对象转换成字典,经过 response 以后变成 json 字符串

1. 创建需要序列化的对象

book_obj = models.MyBook.objects.filter(pk=1).first()

2. 构造序列化器对象

serializer = BookSerializer(instance=book_obj)  # BookSerializer 为序列化类

3. 获取序列化数据
通过data属性可以获取序列化后的数据

serializer.data

4. 如果要被序列化的是包含多条数据的查询集QuerySet,可以通过添加many=True参数补充说明

book_obj = models.MyBook.objects.all()
serializer = BookSerializer(instance=book_obj, many=True)

5. 响应数据给客户端

# 返回的json数据,如果是列表,则需要声明safe=False
return JsonResponse(serializer.data,safe=False)
from rest_framework.response import Response
return Response(serializer.data)

1.5 反序列化

在客户端请求时,使用序列化器可以完成对数据的反序列化
把客户端发送过来的数据,经过 request 以后变成字典,序列化器可以把字典转成模型

1. 数据验证

  • 使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。验证的规则在定义序列化类时已经定义
  • 在获取反序列化的数据前,必须调用 is_valid() 方法进行验证,验证成功返回 True,否则返回 False
  • 验证失败,可以通过序列化器对象的 errors 属性获取错误信息,返回字典,包含了字段和字段的错误
  • 验证成功,可以通过序列化器对象的 validated_data 属性获取数据。
data = request.data  # request.data 返回解析之后的请求体数据
res = BookSerializer(data=data)  # 将要反序列化的数据传递给data构造参数验证
if res.is_valid():
	print(res.validated_data)
else:
    print(res.errors)

is_valid() 方法还可以在验证失败时抛出异常 serializers.ValidationError,可以通过传递raise_exception=True参数开启,REST framework接收到此异常,会向前端返回 HTTP 400 Bad Request响应。

# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)

2. 重写 create() 和 update() 两个方法

数据校验成功后,在使用序列化器来完成数据反序列化的过程中可以创建表记录修改表记录。需要重写 create() 和 update() 两个方法

序列化类

class BookSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    price = serializers.IntegerField(max_value=32)

    def create(self, validated_data):
        return MyBook.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """更新,instance为要更新的对象实例"""
        instance.title = validated_data.get('title', instance.title)
        instance.price = validated_data.get('price', instance.price)
        # 记得保存
        instance.save()
        # 需要返回 instance
        return instance
        

视图函数

class BookDetailView(APIView):
	# 修改记录
    def put(self, request, pk):
        book_obj = models.MyBook.objects.filter(pk=pk).first()
        data = request.data
        res = BookSerializer(instance=book_obj, data=data)
        if res.is_valid():
        	# 该 save 方法调用 update()方法
            res.save()
            return Response(res.data)
        return Response({"msg": "未通过校验"})


class BookView(APIView):
    # 创建记录
    def post(self, request):
        data = request.data
        res = BookSerializer(data=data)
        if res.is_valid():
        	# 该 save 方法调用 create()方法
            res.save()
        return Response(res.data)

路由

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

在上面的代码示例中,视图函数类一个接收了pk参数,一个没有接收。接收了pk参数的类根据该参数生成表对象,并将该对象传给序列化类中的 instance,若没有传递 instance实例,则调用 save() 方法的时候,create() 被调用,相反,如果传递了 instance实例,则调用 save() 方法的时候,update()被调用。

3. save() 方法源码

同样执行 save 方法,其是如何判断是否传入 instance实例 从而调用不同的方法,可以查看其源码,部分源码如下

...
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.'
    )

return self.instance

1.6 钩子函数

除了使用定义序列化类中的校验规则,还可以创建局部钩子函数和全局钩子函数来进行逻辑比较,类似于表单的钩子函数。

在执行 is_valid() 校验时,会先校验字段的校验规则,然后执行局部钩子的校验,在执行全局钩子的校验。

1. 局部钩子

使用validate_字段名的形式作为方法名。校验单个字段

视图函数

class BookDetailView(APIView):
	# 修改记录
    def put(self, request, pk):
        book_obj = models.MyBook.objects.filter(pk=pk).first()
        data = request.data
        res = BookSerializer(instance=book_obj, data=data)
        if res.is_valid():
        	# 该 save 方法调用 update()方法
            res.save()
            return Response(res.data)
        return Response({"msg": "未通过校验"})


class BookView(APIView):
    # 创建记录
    def post(self, request):
        data = request.data
        res = BookSerializer(data=data)
        # raise_exception=True 在接收 ValidationError异常时向前端响应 400
        if res.is_valid(raise_exception=True):
        	# 该 save 方法调用 create()方法
            res.save()
        return Response(res.data)


序列化类

from rest_framework import serializers
class BookSerializer(serializers.Serializer):
	...
	# 钩取 title 字段值,需要手动添加形参
    def validate_title(self, title):
        if title.startswith('ABC'):
            raise serializers.ValidationError("不能以ABC开头")
        else:
            return title
            

2. 全局钩子

全局钩子校验是在局部钩子之后执行。用于校验多个字段。使用validate作为方法名

class BookSerializer(serializers.Serializer):
	...
    def validate(self, attrs):
        title = attrs.get('title')
        price = attrs.get('price')
        if title == price:  # 这是我随便设的条件
            raise serializers.ValidationError('不能相同')
        else:
            return attrs

在全局钩子中出现的参数attrs代表的是校验过后的数据,也就是通过了字段、局部钩子的校验的数据。

2. 序列化类 ModelSerializer

如果使用序列化器对应 Django 的模型类,DRF为我们提供了 ModelSerializer 模型类序列化器来帮助我们快速创建一个 Serializer 类。

ModelSerializer 与常规的 Serializer 相同,但提供了:

  • 基于模型类自动生成一系列字段
  • 基于模型类自动为 Serializer 生成 validators,比如 unique_together
  • 包含默认的 create() 和 update() 的实现

2.1 定义序列化类

from rest_framework import serializers
from .models import MyBook

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyBook
        fields = '__all__'
        
  • 该序列化类需要继承 serializers.ModelSerializer
  • model 指明参照哪个模型类,例如示例中的指定的模型表,可以理解为将模型表中的字段映射成类似于 title = serializers.CharField(max_length=32)
  • fields 指明为模型类的哪些字段生成,'__all__'用于表示全部字段,也可以指定字段fields = ['title', 'price']。还可以排除字段 exclude ['title']

2.2 数据校验

ModelSerializer 序列化类中字段的校验可以进行重写。而局部钩子和全局钩子使用方式并没有变化。

修改字段校验
由于指定了模型表,因此默认使用的是模型表中字段的校验规则,我们在序列化类中可以修改字段的校验,如下所示。

1. 重写字段

在序列化类中直接将字段重写,简单粗暴的方式。需要注意的就是缩进问题,需要放在 class Meta

from rest_framework import serializers
from .models import MyBook

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyBook
        fields = '__all__'

    title = serializers.CharField(max_length=99999)

2. extra_kwargs

我们可以使用 extra_kwargs 参数为 ModelSerializer 添加或修改原有的选项参数。

from rest_framework import serializers
from .models import MyBook


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyBook
        fields = '__all__'

        extra_kwargs = {
            'title': {
            	'max_length': 8, 'min_length': 3, 
            	'error_messages': {'min_length': '太短了'}
            	}
        }

3. 操作模型表

ModelSerializer 序列化类对模型表的记录进行新增和修改不需要重写 create 和 update 方法。因为 ModelSerializer 已经写了,直接使用即可。

3. 序列化多表

当多张表存在外键关系时,序列化的时候还需要加上外键字段。但是展示的时候是展示 id 值,若需要自定义展示的字段,可以如下配置。

模型表

from django.db import models


class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)

    # 关联关系
    publish = models.ForeignKey(to='Publish', to_field='nid', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')  # 自动生成中间表

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = '图书表'


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

    '''
     on_delete可以选择的情况
        -models.CASCADE   		级联删除
        -models.SET_NULL  		关联字段置为空 null=True
        -models.SET_DEFAULT 	关联字段设为默认值 default=0
        -models.DO_NOTHING     	由于数据库有约束会报错,去掉外键关系(公司都不建立外键)
        -on_delete=models.SET(值,函数内存地址)  设置上某个值,或者运行某个函数
    '''

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = '作者表'


class AuthorDetail(models.Model):
    nid = models.AutoField(primary_key=True)
    telephone = models.BigIntegerField()
    birthday = models.DateField()
    addr = models.CharField(max_length=64)

    class Meta:
        verbose_name_plural = '作者详情表'


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

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = '出版社表'

方法一

在模型类中编写方法,并将方法名添加到序列化类的 fields 中。

class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)

    # 关联关系
    publish = models.ForeignKey(to='Publish', to_field='nid', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')  # 自动生成中间表

    def __str__(self):
        return self.name

    def publish_detail(self):
        return {'name': self.publish.name, 'email': self.publish.email}

    def authors_detail(self):
        authors_list = [{'name': i.name, 'age': i.age} for i in self.authors.all()]
        return authors_list

    class Meta:
        verbose_name_plural = '图书表'

方法二

在序列化类中重写字段,使用 serializers.SerializerMethodField,将新字段加入到 fields

from rest_framework import serializers
from .models import Book


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['nid', 'name', 'price', 'authors', 'publish', 'publish_detail', 'authors_detail']
        # 将指定字段设置为只能写
		extra_kwargs = {
            'publish': {'write_only': True},
            'authors': {'write_only': True},
        }
        
	# 重写字段,read_only=True 设置为只读
    publish_detail = serializers.SerializerMethodField(read_only=True)
    authors_detail = serializers.SerializerMethodField(read_only=True)

    def get_publish_detail(self, obj):
        return {'name': obj.name, 'publish': obj.publish.name}

    def get_authors_detail(self, obj):
        authors_list = [{'name': i.name, 'age': i.age} for i in obj.authors.all()]
        return authors_list

serializers.SerializerMethodField 方法专门用来解决多对多取值的问题, 它需要有个配套方法,方法名叫 get_字段名,返回值就是要显示的东西。示例代码中传入的 obj 是 book 对象。

方法三

当外键字段很多的时候,在序列化类中可以使用 depth表示外键关系层级,会将指定层级的内容展示,如下所示

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['nid', 'name', 'price', 'authors', 'publish', ]
        depth = 1

4. 简写序列化类

序列化类

from rest_framework import serializers
from .models import Book, Publish, Author


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['nid', 'name', 'price', 'authors', 'publish', 'publish_detail', 'authors_detail']
        extra_kwargs = {
            'publish': {'write_only': True},
            'authors': {'write_only': True},
        }

    publish_detail = serializers.SerializerMethodField(read_only=True)
    authors_detail = serializers.SerializerMethodField(read_only=True)

    def get_publish_detail(self, obj):
        return {'name': obj.name, 'publish': obj.publish.name}

    def get_authors_detail(self, obj):
        authors_list = [{'name': i.name, 'age': i.age} for i in obj.authors.all()]
        return authors_list


class PublishSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = ['nid', 'name', 'city', 'email', 'book_detail']

    book_detail = serializers.SerializerMethodField(read_only=True)

    def get_book_detail(self, obj):
        book_list = [{'name': i.name, 'price': i.price} for i in obj.book_set.all()]
        return book_list

视图函数

from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView
from app01 import models
from .serializer import BookSerializer, PublishSerializer


class MyView(APIView):
    queryset_obj = None
    serializer_obj = None

    def post(self, request):
        res = self.serializer_obj(data=request.data)
        if res.is_valid():
            res.save()
            return Response(res.data)
        return Response({"code": 100, "msg": "验证不通过", "info": res.errors})

    def get(self, request, **kwargs):
        if kwargs:
            res = self.serializer_obj(self.queryset_obj.filter(**kwargs).first())
        else:
            res = self.serializer_obj(self.queryset_obj.all(), many=True)
        return Response(res.data)

    def delete(self, request, **kwargs):
        obj = self.queryset_obj.filter(**kwargs).delete()
        return Response()

    def put(self, request, **kwargs):
        obj = self.queryset_obj.filter(**kwargs).first()
        data = request.data
        res = self.serializer_obj(obj, data=data)
        if res.is_valid():
            res.save()
            return Response(res.data)
        return Response({"code": 100, "msg": "验证不通过", "info": res.errors})


class BookView(MyView):
    queryset_obj = models.Book.objects
    serializer_obj = BookSerializer


class PublishView(MyView):
    queryset_obj = models.Publish.objects
    serializer_obj = PublishSerializer

路由

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.BookView.as_view()),
    path('publish/', views.PublishView.as_view()),
    path('publish/<int:pk>', views.PublishView.as_view()),
]

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值