一、Django的序列化方法

1、为什么要用序列化组件

做前后端分离的项目,我们前后端数据交互一般都选择JSON,JSON是一个轻量级的数据交互格式。
那么我们给前端数据的时候都要转成json格式,那就需要对我们从数据库拿到的数据进行序列化。

2、表的构建

CHOICES = ((1, "python"), (2, "linux"), (3, "go"))

# 书籍表
class Book(models.Model):
    title = models.CharField(max_length=64)
    category = models.IntegerField(choices=CHOICES)  # 书籍分类
    pub_time = models.DateField()
    publisher = models.ForeignKey(to="Publisher")
    authors = models.ManyToManyField(to="Author")

    class Meta:
        verbose_name = '书籍'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title

# 出版社表
class Publisher(models.Model):
    title = models.CharField(max_length=64)

    class Meta:
        verbose_name = '出版社'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title

# 作者表
class Author(models.Model):
    name = models.CharField(max_length=32)
    class Meta:
        verbose_name = '作者'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

3、希望构建的字典格式

book_list = [
    {
        "id": 1,
        "title": "",
        "publisher": {  # 外键
            "id": 1
            "title": ""
        },
        "authors": [{}, {}]  # 多对多
    },
    {
        "id": 2,
        "title": "",
        "publisher": {  # 外键
            "id": 1
            "title": ""
        },
        "authors": [{}, {}]  # 多对多
    },

]

4、方法一

在Django中使用Json模块序列化
class BooksView(views.View):
    def get(self, request):
        book_queryset = Book.objects.values("id", "title", "pub_time", "publisher")
        book_list = list(book_queryset)  # 把queryset类型转换成列表
        # 如果我们需要取外键关联的字段信息 需要循环获取外键 再去数据库查然后拼接成我们想要的
        ret = []
        for book in book_list:
            publisher_obj = Publisher.objects.filter(id=book["publisher"]).first()
            # 修改原字典中的publisher对应的值
            book["publisher"] = {
                "id": publisher_obj.id,
                "title": publisher_obj.title
            }
            # 把新的字典追加到一个空列表中
            ret.append(book)
        ret = json.dumps(ret, ensure_ascii=False, cls=MyJson)
        return HttpResponse(ret)

# json不能序列化时间字段,重写JSONEncoder里面的default方法解决
class MyJson(json.JSONEncoder):
    def default(self, field):
        if isinstance(field, datetime.datetime):
            return field.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(field, datetime.date):
            return field.strftime('%Y-%m-%d')
        else:
            return json.JSONEncoder.default(self, field)

5、方法二

使用JsonResponse,自动帮我们重写了JSONEncoder里面的default方法,解决时间字段的问题
class BooksView(views.View):
    def get(self, request):
        book_queryset = Book.objects.values("id", "title", "pub_time", "publisher")
        book_list = list(book_queryset)  # 把queryset类型转换成列表
        # 如果我们需要取外键关联的字段信息 需要循环获取外键 再去数据库查然后拼接成我们想要的
        ret = []
        for book in book_list:
            publisher_obj = Publisher.objects.filter(id=book["publisher"]).first()
            # 修改原字典中的publisher对应的值
            book["publisher"] = {
                "id": publisher_obj.id,
                "title": publisher_obj.title
            }
            # 把新的字典追加到一个空列表中
            ret.append(book)
        return JsonResponse(ret, safe=False, json_dumps_params={'ensure_ascii': False})

6、方法三

使用Django自带的序列化模块
from django.core import serializers

# 能够得到我们要的效果 结构有点复杂
class BooksView(views.View):
    def get(self, request):
        book_queryset = Book.objects.all()
        ret = serializers.serialize("json", book_queryset, ensure_ascii=False)
        return HttpResponse(ret)

二、DRF序列化的介绍

1、介绍

下载DRF模块:pip install djangorestframework
导入:
    from rest_framework.views import APIView
    from rest_framework.response import Response

使用DRF默认的页面,需要在settings的APP注册rest_framework
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
]
首先,我们要用DRF的序列化,就要遵循DRF框架的一些标准,
  -- Django我们CBV继承类是View,现在DRF我们要用APIView
  -- Django中返回的时候我们用HTTPResponse,JsonResponse,render ,DRF我们用Response

2、APIView跟View区别

-- APIView继承了View
-- APIView的as_view方法实现了csrf中间件的豁免(用DRF不需要再把settings的csrf中间件注释掉了)
-- 重新封装了request
    request._request可以拿到旧的request
    request.query_params  旧的request.GET即_request.GET
    request.data    除了GET请求外的所有的数据,_request.POST、_request.FILES等-- 序列化器对象.data  存放序列化好的数据  校验通过的数据存放到validated_data里,最后也会序列化封装到序列化对象.data里面返回给前端  校验不通过,错误信息存到序列化对象.errors里面-- DRF有自己的序列化模块  from rest_framework import serializers
-- Response
  继承了HttpResponse
  携带HTTP标准状态码
  做模板的渲染

3、使用方法

-- 序列化(传数据到前端)
    -- 声明一个序列化器
        class BookSerializer(serializers.Serializer):
            id = serializers.IntegerField(required=False)
            title = serializers.CharField(max_length=32)
            pub_time = serializers.DateField()
    -- 视图里序列化我们的queryset或者某个对象
        # queryset需要声明many=True
        ser_obj = BookSerializer(queryset, many=True)
        # 具体的某个的对象则不需要声明
        ser_obj = BookSerializer(book_obj)
        return Response(ser_obj.data)
    -- 实现流程
        -- 如果指定了many=True
        -- 把queryset当成可迭代对象去循环 得到每个模型对象
        -- 把每个模型对象放入序列号器进行序列化
        -- 进行字段匹配 匹配上的字段进行序列化 匹配不上丢弃
        -- 必须满足序列化的所有字段要求

-- 反序列化(从前端获取数据)
    -- 获取前端传过来的数据
    -- 用序列化器进行校验
        # 新增一条数据
        ser_obj = BookSerializer(data=request.data)
        # 编辑某条数据
        # instance编辑哪个对象,data前端传过来要跟新的某些字段数据,partial表示允许部分跟新
        ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True)
        if ser_obj.is_valid():
            ser_obj.save()# 调用create/update方法
            return Response(ser_obj.data)
        else:
            return Response(ser_obj.errors)
    -- 写create方法
        在create方法里用ORM操作创建新对象
    -- 写update方法
        在update方法里用ORM操作创编辑对象

4、注意事项

1. 外键和多对多字段的序列化需要额外再设置序列化器
2. 序列化
    序列化器的参数是queryset和many
3. 反序列化
    序列化器的参数是data=提交上来的数据
4. 序列化器字段类型不统一的情况
    反序列化要用的一些字段通过一些参数跟序列化区分开
    -- required=False
    -- read_only=True
    -- write_only=True

5. 反序列化的验证
    is_valid()       --> 校验数据
    post请求中的save()  --> 调用序列化器的create方法  put请求中的save()    --> 调用序列化器的update方法

三、DRF序列化示例

1、声明序列化器

# serializers.py文件

from rest_framework import serializers
from libsys.models import Book

CHOICES = ((1, "python"), (2, "linux"), (3, "go"))

# 继承serializers.Serializer
class PublisherSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=64)

class AuthorSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)

# 字段的声明和models相类似
class BookSerializer(serializers.Serializer):
    # POST校验的时候required=False声明不需要校验这个字段
    id = serializers.IntegerField(required=False)

    title = serializers.CharField(max_length=64)
    pub_time = serializers.DateField()

    # 选择字段,显示的是数字
    # category = serializers.ChoiceField(choices=CHOICES)
    # 把选择字段显示成字符类型,source参数后面跟的是ORM操作
    # read_only=True表示这个字段只在渲染前端的时候使用
    category = serializers.CharField(source='get_category_display', read_only=True)
    # write_only=True表示这个字段只在POST提交数据,做校验的时候使用
    post_category = serializers.IntegerField(write_only=True)

    # 外键需要设置额外的序列化器对它进行序列化
    publisher = PublisherSerializer(read_only=True)
    publisher_id = serializers.IntegerField(write_only=True)

    # 多对多字段需要设置额外的序列化器对它进行序列化,且声明many=True
    authors = AuthorSerializer(many=True, read_only=True)
    author_list = serializers.ListField(write_only=True)

    def create(self, validated_data):  # validated_data是通过校验的数据,最后也会封装到data里面
        #通过ORM操作给Book表增加数据
        book_obj = Book.objects.create(title=validated_data["title"], pub_time=validated_data["pub_time"],
                            category=validated_data["post_category"], publisher_id=validated_data["publisher_id"])
        book_obj.authors.add(*validated_data["author_list"])
        return book_obj

    def update(self, instance, validated_data):
        # 通过ORM操作给Book表编辑数据
        # instance就是book_obj
        instance.title = validated_data.get("title", instance.title)
        instance.pub_time = validated_data.get("pub_time", instance.pub_time)
        instance.category = validated_data.get("post_category", instance.category)
        instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)
        if validated_data.get("author_list", False):
            instance.authors.set(validated_data["author_list"])
        instance.save()
        return instance

2、在视图函数中调用

from libsys.models import Book, Publisher, Author
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import BookSerializer

# Create your views here.

# 书籍列表
class BookView(APIView):
    def get(self, request):
        book_queryset = Book.objects.all()
        # 声明一个序列化器
        # 用序列化器去序列化queryset(queryset有多个对象的时候,需要声明many=True)
        # 把数据提交到序列化器,跟序列化器的字段进行匹配,匹配成功就进行序列化
        ser_obj = BookSerializer(book_queryset, many=True)
        return Response(ser_obj.data)

    # 新增书籍
    def post(self, request):
        # 前端传过来的数据应该也是这样的
        # {"id""title",
        #       category: 1
        #       publisher: 1
        #       authors: [1, 2]
        #  }

        # 获取前端传过来的数据
        book_obj = request.data
        # 用序列化器做校验
        ser_obj = BookSerializer(data=book_obj)
        if ser_obj.is_valid():
            # 校验通过,新增书籍
            ser_obj.save()  # 这里的save方法会去调用序列化器的create方法
            print(ser_obj.validated_data)  # validated_data是通过校验的数据,也会封装到data里面
            return Response(ser_obj.data)
        # 校验不通过返回错误信息
        return Response(ser_obj.errors)

# 编辑书籍
class BookEditView(APIView):
    def get(self, request, id):
        book_obj = Book.objects.filter(pk=id).first()
        ser_obj = BookSerializer(book_obj)
        return Response(ser_obj.data)

    def put(self, request, id):
        book_obj = Book.objects.filter(id=id).first()
        ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True)
        if ser_obj.is_valid():
            ser_obj.save()
            return Response(ser_obj.data)
        return Response(ser_obj.errors)

四、验证

1、 单个字段的验证(局部钩子)

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    title = serializers.CharField(max_length=64)
    其他字段...

    # 局部钩子方法:validate_字段名,value 是提交过来的这个字段的数据
    def validate_title(self, value):
        # 定义校验规则:标题必须含有python、linux、go
        course_list = ['python', 'linux', 'go']
        for course in course_list:
            if course in value.lower():
                return value
        else:
            raise serializers.ValidationError('输入的书籍名不合法')

2、 多个字段的验证(全局钩子)

class BookSerializer(serializers.Serializer):
    post_category = serializers.IntegerField(write_only=True)
    publisher_id = serializers.IntegerField(write_only=True)
    其他字段...

    # 全局钩子方法:validate,attrs 是前端传过来的所有的数据组成的字典
    def validate(self, attrs):
        # 定义校验规则:书籍分类和作者id不能超过3
        if attrs['post_category'] > 3 or attrs['publisher_id'] > 3:
            raise serializers.ValidationError('输入的图书分类或作者不存在')
        return attrs

3、 自定义校验规则

def my_validate(value):
    if '周星星' in value:
        raise serializers.ValidationError('输入的书籍太帅,不合法')
    return value

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    # validators声明校验的规则
    title = serializers.CharField(max_length=64, validators=[my_validate,])

4、校验的权重

自定义校验 > 局部钩子 > 全局钩子

五、ModelSerializer

1、介绍

跟Django的Form组件类似,我们使用DRF进行前后端数据交互,有很多需要序列化和反序列化的字段都跟models模型相关,
那么,DRF也给我们提供了跟模型紧密相关的序列化器:ModelSerializer
-- 继承serializers.ModelSerializer
-- 它和Form有点类似
-- 它会根据模型自动生成一组字段
-- 它默认实现了.update()以及.create()方法

2、定义ModelSerializer序列化器

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"
        # fields = ["id", "title", "pub_time"]
        # exclude = ["user"]
        # 分别是所有字段 包含某些字段 排除某些字段

3、外键关系的字段

当序列化类META中定义了depth时,这个序列化类中引用字段(外键)则自动变为只读

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"
        # fields = ["id", "title", "pub_time"]
        # exclude = ["user"]
        # 分别是所有字段 包含某些字段 排除某些字段

        # depth 代表找嵌套关系的第几层
        depth = 1

4、META中的其他参数

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"
        # fields = ["id", "title", "pub_time"]
        # exclude = ["user"]
        # 分别是所有字段 包含某些字段 排除某些字段

        # depth 代表找嵌套关系的第几层
        depth = 1

        # 只读字段
         read_only_fields = ["id"]

         # 给某些字段设置额外参数
        extra_kwargs = {"title": {"validators": [my_validate,]}}

5、SerializerMethodField方法字段

外键关联的对象有很多字段我们是用不到的,如果都传给前端会有数据冗余,
就需要我们自己去定制序列化外键对象的哪些字段。

使用了方法字段,它会自动去找对应的钩子函数(get_字段名),这个方法字段展示的值就是钩子函数的返回值
钩子函数的参数obj:就是序列化的每个模型对象 book_obj

class BookSerializer(serializers.ModelSerializer):
    # 方法字段
    # SerializerMethodField 会去找钩子方法 钩子方法的返回值给这个字段
    # 钩子函数:get_字段名称
    category_text = serializers.SerializerMethodField(read_only=True)
    publisher_info = serializers.SerializerMethodField(read_only=True)
    author_info = serializers.SerializerMethodField(read_only=True)

    def get_category_text(self, obj):
        # obj就是序列化的每个模型对象 book_obj
        return obj.get_category_display()

    def get_publisher_info(self, obj):
        return {"id": obj.publisher_id, "title": obj.publisher.title}

    def get_author_info(self, obj):
        return [{"id": author.id, "name": author.name} for author in obj.authors.all()]

    class Meta:
        model = Book
        fields = "__all__"

6、完整的ModelSerializer思路

因为depth会让我们外键变成只读,所以一般来说我们不用它,
因为如果是前端发数据过来(post,put等),我们对外键、多对多等字段进行验证是验证其在数据库的中真实值,
因此,把ModelSerializer序列化器fields代表的字段应该用于反序列化,而对序列化到前端的外键、多对多等字段的数据,
我们可以使用SerializerMethodField对其进行处理后展示到前端。

class BookSerializer(serializers.ModelSerializer):
    # 方法字段
    category_text = serializers.SerializerMethodField(read_only=True)
    publisher_info = serializers.SerializerMethodField(read_only=True)
    author_info = serializers.SerializerMethodField(read_only=True)
    # 方法字段的值,取决于它对应的方法字段的钩子函数的返回值
    def get_category_text(self, obj):
        # obj就是序列化的每个模型对象 book_obj
        return obj.get_category_display()

    def get_publisher_info(self, obj):
        return {"id": obj.publisher_id, "title": obj.publisher.title}

    def get_author_info(self, obj):
        return [{"id": author.id, "name": author.name} for author in obj.authors.all()]

    class Meta:
        model = Book
        fields = "__all__"
        # fields = ["id", "title", "pub_time"]
        # exclude = ["user"]
        # 分别是所有字段 包含某些字段 排除某些字段

        # depth 代表找嵌套关系的第几层
        # depth = 1

        extra_kwargs = {
            "category": {'write_only': True},
            "publisher": {'write_only': True},
            "authors": {'write_only': True},
        }

DRF初识与序列化的更多相关文章

  1. DRF中的序列化器

    DRF中的序列化器详细应用   视图的功能:说白了就是接收前端请求,进行数据处理 (这里的处理包括:如果前端是GET请求,则构造查询集,将结果返回,这个过程为序列化:如果前端是POST请求,假如要对数 ...

  2. 【DRF框架】序列化组件

    DRF框架的序列化组件 在前后端分离的应用模式中,后端仅返回前端所需的数据,返回的数据类似是JSON,因此需要使用序列化组件进行序列化再将数据返回 使用JsonResponse做序列化 #  使用Js ...

  3. DRF分阶段序列化细化实例

    DRF是分两阶段进行的. 如果是API接收数据,则是先进行Parser,将外界接收的bytes数据分析成python数据类型,其间涉及encoding操作,再进行序列化,将python数据类型保存进数 ...

  4. drf:筛选,序列化

    1.基础 restful规范: - url:一般用名词 http://www.baidu.com/article (面向资源编程) - 根据请求方式的不同做不同操作:get,post,put,dele ...

  5. drf图片字段序列化完整路径

    一.需求 前端需要它想要的数据格式: 原有的数据格式: 二.定制化: 1.可以嵌套序列化pol_type,lit_des,area_detail,但结构如下: class ChrDetailSeria ...

  6. DRF单表序列化和反序列化

    安装 djangorestframework pip install djangorestframework 将rest_framework注册到app上 INSTALLED_APPS = [ 're ...

  7. 【DRF框架】序列化组件——ModelSerializer

    ModelSerializer 1.ModelSerializer类似于ModelForm 2.根据模型自动生成一组字段 3.自带实现了.update()以及.create()方法 ModelSeri ...

  8. 【DRF框架】序列化组件——字段验证

    单个字段的验证 1.在序列化器里定义校验字段的钩子方法   validate_字段 2.获取字段的数据 3.验证不通过,抛出异常  raise serializers.ValidationError( ...

  9. DRF初识

    目录 Web API接口 什么是Web API接口 接口四大特征 接口文档的编写测试 restful接口规范 url链接设计 五大请求方式 响应结果 DRF框架安装 基于原生Django实现十大接口 ...

随机推荐

  1. .htaccess的301重定向代码

    把不带www的域名301到带www的域名 RewriteEngine On RewriteCond %{http_host} ^example.com$ [NC] RewriteRule ^(.*)$ ...

  2. spring基于注解的配置文件

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  3. 关于js跨域

    get方式: 称为jsonp,就是js的跨域通信方式,因为知道有些标签可以跨域获取内容,例如img,script,link...,jsonp就是把动态创建一个script标签,然后配置src属性,后台 ...

  4. Java Calendar获取年、月、日、时间

    Java Calendar获取年.月.日.时间 Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT+08:00" ...

  5. shiro基础学习(二)&mdash;shiro认证

    一.shiro简介      shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证.权限授权.加密.会话管理等功能,组成了一个通用的安全认证框架. 以下 ...

  6. 80端口被系统服务【kernel&amp;System】占用解决方案

    netstat -ano | findstr port    //查看端口占用情况 tasklist | findstr port   //查看端口被占用的具体服务名 运行net stop http ...

  7. mysql 连接 的drive 不一样

          DB_DRIVER 为任意名 com.mysql.jdbc.Driver 是 mysql-connector-java 5(jar包)com.mysql.cj.jdbc.Driver 是 ...

  8. 【LeetCode】134.Gas Station

    Problem: There are N gas stations along a circular route, where the amount of gas at station i is ga ...

  9. MySql(十四):MySql架构设计——可扩展性设计之数据切分

    一.前言 通过 MySQL Replication 功能所实现的扩展总是会受到数据库大小的限制,一旦数据库过于庞大,尤其是当写入过于频繁,很难由一台主机支撑的时候,我们还是会面临到扩展瓶颈.这时候,我 ...

  10. 敏感词过滤和XML的创建

    今天我慢下来啦,因为这三天没有新的课程学习内容,自己仅仅看啦一些,这让我停下来栖息片刻:说说现在的生活,简单的进行着,每天要奔波着去上课,然后回来,每天都在想怎样学习这个小知识点,大脑也在想怎样解决程 ...