前后端分离后,其交互一般都选择使用JSON数据格式,JSON是一个轻量级的数据交互格式.

因此,后端发送给前端(或前端发送给后端)的数据都要转成JSON格式,这就得需要我们把从数据库内取到的数据进行序列化.

本文将详细讲述Django项目中如何使用第三方库rest_framework进行序列化.

在命令行中输入:pip install djangorestframework,方可下载rest_framework.

@


首先,我们准备三张数据表:

  1. from django.db import models
  2. __all__ = ['Book', 'Publisher', 'Author']
  3. class Book(models.Model):
  4. """书籍表"""
  5. title = models.CharField(max_length=62)
  6. CHOICES = ((1, '前端'), (2, '后端'), (3, '运维'))
  7. category = models.IntegerField(choices=CHOICES)
  8. pub_date = models.DateField() # 出版日期
  9. publisher = models.ForeignKey(to='Publisher') # 外键出版社表
  10. authors = models.ManyToManyField(to='Author') # 多对多作者表
  11. class Publisher(models.Model):
  12. """出版社表"""
  13. title = models.CharField(max_length=64)
  14. class Author(models.Model):
  15. """作者表"""
  16. name = models.CharField(max_length=64)

插入数据:

  1. # 在Python脚本中调用Django环境:
  2. import os, datetime
  3. if __name__ == '__main__':
  4. # 请将'blog091.settings'更改为对应的项目名称及配置文件,更改后直接运行即可
  5. os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'blog091.settings')
  6. import django
  7. django.setup()
  8. from blog import models
  9. # 添加出版社
  10. pub01 = models.Publisher.objects.create(title="上帝出版社")
  11. pub02 = models.Publisher.objects.create(title="沙河出版社")
  12. pub03 = models.Publisher.objects.create(title="西二旗出版社")
  13. # 添加作者
  14. au01 = models.Author.objects.create(name="迷之标签大仙")
  15. au02 = models.Author.objects.create(name="迷之重启大仙")
  16. au03 = models.Author.objects.create(name="迷之算法大仙")
  17. # 添加书籍
  18. pub_date = {'year': 2099, 'month': 12, 'day': 31}
  19. book01 = models.Book.objects.create(title="【论写标签的姿势】", category=1, pub_date=datetime.date(**pub_date),
  20. publisher=pub01)
  21. book01.authors.add(au01)
  22. book02 = models.Book.objects.create(title="【论重启服务的姿势】", category=2, pub_date=datetime.date(**pub_date),
  23. publisher=pub02)
  24. book02.authors.add(au02)
  25. book03 = models.Book.objects.create(title="【论写算法的姿势】", category=3, pub_date=datetime.date(**pub_date),
  26. publisher=pub03)
  27. book03.authors.add(au03)

基本的序列化操作

既然我们要使用DRF的序列化,那么我们就得遵循人家框架的一些标准.

  • 在Django中,我们的CBV继承类是View;而在DRF中,我们的继承类时APIView.
  • 在Django中,我们返回数据使用HTTPResponse、JsonResponse、render;而在DRF中,我们使用Response.

第一步 注册app

第二步 新建一个py文件,在文件内声明序列化类

  1. from rest_framework import serializers
  2. class BookSerializer(serializers.Serializer):
  3. id = serializers.IntegerField()
  4. title = serializers.CharField(max_length=32)
  5. # 我们先注释掉一个字段
  6. # 然后你读一读下面第二行黄色的字,再看一看第四步骤中的图,你就明白了.
  7. # pub_date = serializers.DateField()
  8. category = serializers.CharField(source='get_category_display')

这里面写的字段必须是在models文件中存在的.

并且,在models文件中有,而在这里没有的字段会被过滤掉.

第三步 使用CBV,序列化对象

  1. from blog import models
  2. # 继承类
  3. from rest_framework.views import APIView
  4. # 返回数据的方法
  5. from rest_framework.response import Response
  6. # 导入上一步骤定义的序列化类
  7. from .serializers import BookSerializer
  8. class Book(APIView):
  9. def get(self, request):
  10. # 首先,我们获取书籍列表
  11. book_list = models.Book.objects.all()
  12. # 然后,将书籍列表传入序列化类
  13. ret = BookSerializer(book_list, many=True)
  14. # book_list:要过滤的对象列表(过滤的是字段,而不是对象)
  15. # many=True:表示取多个(源码中会循环取)
  16. # 最后返回数据
  17. # 数据在其data中,所以要返回ret.data
  18. return Response(ret.data)

第四部 启动项目,访问站点



怎么样,四不四很66啊.


外键/多对多关系的序列化

在声明序列化类的文件中添加如下代码:

  1. from rest_framework import serializers
  2. class PublisherSerializer(serializers.Serializer):
  3. id = serializers.IntegerField()
  4. title = serializers.CharField(max_length=64)
  5. class AuthorSerializer(serializers.Serializer):
  6. id = serializers.IntegerField()
  7. name = serializers.CharField(max_length=32)
  8. class BookSerializer(serializers.Serializer):
  9. id = serializers.IntegerField()
  10. title = serializers.CharField(max_length=32)
  11. pub_date = serializers.DateField()
  12. category = serializers.CharField(source='get_category_display')
  13. # 出版社(外键关系) 指定上面写的出版社类即可
  14. publisher = PublisherSerializer()
  15. # 内部通过外键关系的id找到publisher_object
  16. # 再通过PublisherSerializer(publisher_object)得到数据
  17. # 作者(多对多关系) 指定上面写的作者类即可
  18. authors = AuthorSerializer(many=True)
  19. # many=True:表示取多个(源码中会循环取)

然后,我们打开浏览器,狂点刷新后,即可看到:


反序列化的操作

当前端给我们传送数据的时候(post请求),我们要进行一些校验然后保存到数据库.

DRF中的Serializer给我们提供了一些校验和保存数据的方法,首先我们写出反序列化用到的一些字段,有些字段要跟序列化区分开来,基本步骤如下.

步骤一 声明反序列化类

·

序列化和反序列化的字段不统一,因此我们需要分别定义序列化和反序列化的字段

  1. from rest_framework import serializers
  2. from blog import models
  3. class PublisherSerializer(serializers.Serializer):
  4. id = serializers.IntegerField()
  5. title = serializers.CharField(max_length=64)
  6. class AuthorSerializer(serializers.Serializer):
  7. id = serializers.IntegerField()
  8. name = serializers.CharField(max_length=32)
  9. class BookSerializer(serializers.Serializer):
  10. # 序列化和反序列化的字段不统一,因此必须分别定义序列化和反序列化的字段
  11. """
  12. 我们先了解如下三个参数:
  13. required=False -> 反序列化时不校验此字段
  14. read_only=True -> 反序列化时不校验此字段
  15. write_only=True -> 序列化时不校验此字段
  16. """
  17. # ==== 指定序列化时校验,而反序列化时不校验的字段 ====
  18. id = serializers.IntegerField(required=False)
  19. category = serializers.CharField(source='get_category_display', read_only=True)
  20. publisher = PublisherSerializer(read_only=True)
  21. authors = AuthorSerializer(many=True, read_only=True)
  22. # ==== 始终校验的字段 ====
  23. title = serializers.CharField(max_length=64)
  24. pub_date = serializers.DateField()
  25. # ==== 指定反序列化时校验,而序列化时不校验的字段 ====
  26. post_category = serializers.IntegerField(write_only=True)
  27. publisher_id = serializers.IntegerField(write_only=True)
  28. author_list = serializers.ListField(write_only=True)
  29. def create(self, validated_data):
  30. """
  31. 用于增加数据的方法
  32. :param validated_data: 校验通过的数据(就是步骤二中传过来的book_obj)
  33. :return: validated_data
  34. """
  35. # 开始ORM操作:
  36. book_obj = models.Book.objects.create(
  37. title=validated_data['title'],
  38. pub_date=validated_data['pub_date'],
  39. category=validated_data['post_category'],
  40. publisher_id=validated_data['publisher_id']
  41. )
  42. book_obj.authors.add(*validated_data['author_list'])
  43. return validated_data

步骤二 使用CBV 反序列化对象

  1. from blog import models
  2. # 继承类
  3. from rest_framework.views import APIView
  4. # 返回数据的方法
  5. from rest_framework.response import Response
  6. # 导入上一步骤定义的序列化类
  7. from .serializers import BookSerializer
  8. class Book(APIView):
  9. def get(self, request):
  10. book_list = models.Book.objects.all()
  11. ret = BookSerializer(book_list, many=True)
  12. return Response(ret.data)
  13. # 重点在这里:
  14. def post(self, request):
  15. """post请求的基本思路:确定数据类型以及数据结构,对前端传过来的数据进行校验"""
  16. book_obj = request.data # 提取post请求发过来的数据
  17. ser_obj = BookSerializer(data=book_obj)
  18. # data=book_obj:指定反序列化数据
  19. if ser_obj.is_valid(): # 开始校验
  20. ser_obj.save() # 保存数据
  21. return Response(ser_obj.validated_data) # 校验成功,返回校验信息
  22. return Response(ser_obj.errors) # 校验失败,返回错误信息

步骤三 启动项目 访问页面并提交JSON数据



成功后,会展示出提交的数据,如下:


单条数据查询及更新

步骤一 准备url

  1. from blog import views
  2. urlpatterns = [
  3. url(r'^book/(?P<book_id>\d+)$', views.BookEdit.as_view()),
  4. ]

步骤二 声明(反)序列化类

  1. from rest_framework import serializers
  2. from blog import models
  3. class PublisherSerializer(serializers.Serializer):
  4. id = serializers.IntegerField()
  5. title = serializers.CharField(max_length=64)
  6. class AuthorSerializer(serializers.Serializer):
  7. id = serializers.IntegerField()
  8. name = serializers.CharField(max_length=32)
  9. class BookSerializer(serializers.Serializer):
  10. # 序列化和反序列化的字段不统一,因此必须分别定义序列化和反序列化的字段
  11. """
  12. 我们先了解如下三个参数:
  13. required=False -> 反序列化时不校验此字段
  14. read_only=True -> 反序列化时不校验此字段
  15. write_only=True -> 序列化时不校验此字段
  16. """
  17. # ==== 指定序列化时校验,而反序列化时不校验的字段 ====
  18. id = serializers.IntegerField(required=False)
  19. category = serializers.CharField(source='get_category_display', read_only=True)
  20. publisher = PublisherSerializer(read_only=True)
  21. authors = AuthorSerializer(many=True, read_only=True)
  22. # ==== 始终校验的字段 ====
  23. title = serializers.CharField(max_length=64)
  24. pub_date = serializers.DateField()
  25. # ==== 指定反序列化时校验,而序列化时不校验的字段 ====
  26. post_category = serializers.IntegerField(write_only=True)
  27. publisher_id = serializers.IntegerField(write_only=True)
  28. author_list = serializers.ListField(write_only=True)
  29. def update(self, instance, validated_data):
  30. """
  31. 用于更新数据的方法
  32. :param instance: 要更新的数据就是步骤三中传过来的book_obj)
  33. :param validated_data: 校验通过的数据
  34. :return: instance
  35. """
  36. # 开始ORM操作:
  37. instance.title = validated_data.get('title', instance.title)
  38. instance.pub_date = validated_data.get('pub_date', instance.pub_date)
  39. instance.category = validated_data.get('category', instance.category)
  40. instance.publisher_id = validated_data.get('publisher_id', instance.publisher_id)
  41. if validated_data.get('author_list'):
  42. instance.authors.set(validated_data['author_list'])
  43. instance.save()
  44. return instance

步骤三 使用CBV (反)序列化对象

  1. class BookEdit(APIView):
  2. def get(self, request, book_id):
  3. # 1. 获取指定id的数据
  4. book_obj = models.Book.objects.filter(id=book_id).first()
  5. # 2. 过滤字段(序列化)
  6. ser_obj = BookSerializer(book_obj)
  7. # 3. 返回数据,数据在data中
  8. return Response(ser_obj.data)
  9. # put请求
  10. def put(self, request, book_id):
  11. book_obj = models.Book.objects.filter(id=book_id).first() # 先获取要更新的数据
  12. ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True)
  13. # instance=book_obj:要更新的数据
  14. # data=request.data:新的数据
  15. # partial=True:部分校验
  16. if ser_obj.is_valid():
  17. ser_obj.save()
  18. return Response(ser_obj.validated_data) # 校验成功,返回校验信息
  19. return Response(ser_obj.errors) # 校验失败,返回错误信息

步骤四 启动项目 查询指定id的数据



这就是单条数据查询.

步骤五 更新指定id的数据



提交成功后,将展示出更新的数据,如下:


数据的校验

DRF为我们提供了钩子函数,可用于校验单个或多个字段.

单个字段的校验

在(反)序列化类中添加钩子函数.

  1. def validate_title(self, value):
  2. """
  3. validate_字段名, 对单个字段进行校验
  4. :param value: 对应提交的title的值
  5. :return:
  6. """
  7. if "之" not in value:
  8. raise serializers.ValidationError("书名必须含有 之")

此时,添加/更新书籍时,如果书名中没有"之"字,则将抛出错误信息:

多个字段的校验

在(反)序列化类中添加钩子函数.

  1. def validate(self, attrs):
  2. """
  3. 可对对所有字段进行校验
  4. :param attrs: 提交的所有数据
  5. :return: 校验通过时返回attrs
  6. """
  7. if attrs['post_category'] == 3 and attrs['pub_date'] == '2099-12-31':
  8. return attrs
  9. else:
  10. raise serializers.ValidationError("类型必须指定 3,且出版日期必须为 2099-12-31")

此时,添加/更新书籍时,如果不符合校验规则,则将抛出错误信息.

自定义校验器

  1. # 首先,定义一个校验器函数
  2. def my_validate(value):
  3. """自定义的校验器函数"""
  4. if "敏感信息" in value.lower():
  5. raise serializers.ValidationError("内容包含敏感词汇!")
  6. class BookSerializer(serializers.Serializer):
  7. # 然后,在校验器中调用此函数
  8. title = serializers.CharField(max_length=64, validators=[my_validate,])
  9. # validators=[my_validate,]:指定校验函数列表

现在,我们已经很清楚Serializer的用法了,然而我们发现,所有的序列化都跟我们的模型紧密相关...

对,没错,DRF也给我们提供了跟模型紧密相关的序列化器——ModelSerializer.


终极用法 ModelSerializer

根据模型自动生成字段.

默认就实现了添加与更新的方法.

以下示例将实现上述的所有功能:

  1. from rest_framework import serializers
  2. from blog import models
  3. class PublisherSerializer(serializers.Serializer):
  4. id = serializers.IntegerField()
  5. title = serializers.CharField(max_length=64)
  6. class AuthorSerializer(serializers.Serializer):
  7. id = serializers.IntegerField()
  8. name = serializers.CharField(max_length=32)
  9. def my_validate(value):
  10. """自定义的校验器函数"""
  11. if "敏感信息" in value.lower():
  12. raise serializers.ValidationError("内容包含敏感词汇!")
  13. class BookSerializer(serializers.ModelSerializer):
  14. """ 重写字段 + def get_自定义字段名(self, obj): """
  15. # 重写的字段如果与原字段同名,则会覆盖掉原字段
  16. # 外键关联的对象有很多字段我们是用不到的, 都传给前端会有数据冗余, 就需要我们自己去定制序列化外键对象的哪些字段
  17. publisher_info = serializers.SerializerMethodField(read_only=True)
  18. def get_publisher_info(self, obj):
  19. # obj是要序列化的每个对象
  20. return {'id': obj.publisher.id, 'title': obj.publisher.title}
  21. authors_info = serializers.SerializerMethodField(read_only=True)
  22. def get_authors_info(self, obj):
  23. return [{'id': author.id, 'name': author.name} for author in obj.authors.all()]
  24. # 再比如我们的选择字段,默认显示的是key, 而我们要展示给用户的是value, 因此,我们重写选择字段来自定制:
  25. category_dis = serializers.SerializerMethodField(read_only=True)
  26. def get_category_dis(self, obj):
  27. return obj.get_category_display()
  28. class Meta:
  29. """ 指定数据表 """
  30. model = models.Book
  31. """ 获取字段 """
  32. fields = "__all__" # 所有字段
  33. # fields = ['id', 'title', '...'] # 包含指定字段
  34. # exclude=["id", '...'] # 排除指定字段
  35. """ depth """
  36. # 代表找嵌套关系的第几层,指定找外键关系向下找几层
  37. # depth = 1
  38. # 会将所有的外键关系变成只读read_only=True
  39. # 这个参数几乎不用,最好不要错过4层
  40. """ 只读字段 """
  41. # 即反序列化时不校验的字段
  42. read_only_fields = ['id', ]
  43. """ extra_kwargs """
  44. # 按照下面的格式, 可为所有字段设置参数
  45. extra_kwargs = {
  46. "title": {"validators": [my_validate, ]},
  47. 'publisher': {'write_only': True},
  48. 'authors': {'write_only': True}
  49. }

【DRF序列化】的更多相关文章

  1. DRF 序列化组件

    Serializers 序列化组件 Django的序列化方法 class BooksView(View): def get(self, request): book_list = Book.objec ...

  2. python 全栈开发,Day99(作业讲解,DRF版本,DRF分页,DRF序列化进阶)

    昨日内容回顾 1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确. - API (IOS,安卓,PC,微信小程序...) - vue.js等框架编写前端时,会比之前写jQuery ...

  3. python 全栈开发,Day95(RESTful API介绍,基于Django实现RESTful API,DRF 序列化)

    昨日内容回顾 1. rest framework serializer(序列化)的简单使用 QuerySet([ obj, obj, obj]) --> JSON格式数据 0. 安装和导入: p ...

  4. Django的DRF序列化方法

    安装rest_framework -- pip install djangorestframework -- 注册rest_framework序列化 -- Python--json -- 第一版 用v ...

  5. drf序列化器与反序列化

    什么是序列化与反序列化 """ 序列化:对象转换为字符串用于传输 反序列化:字符串转换为对象用于使用 """ drf序列化与反序列化 &qu ...

  6. drf序列化及反序列化

    假如把drf看做一个汉堡包,我们之前讲的模块属于汉堡包前面的盖盖(请求模块.渲染模块)和底底(异常模块.解析模块.响应模块),但是真正中间的夹心没有讲,那么今天我就和大家来看一下汉堡包的夹心(序列化及 ...

  7. drf序列化和反序列化

    目录 drf序列化和反序列化 一.自定义序列化 1.1 设置国际化 二.通过视图类的序列化和反序列化 三.ModelSerializer类实现序列化和反序列化 drf序列化和反序列化 一.自定义序列化 ...

  8. DRF 序列化组件 模型层中参数补充

    一. DRF序列化 django自带有序列化组件,但是相比rest_framework的序列化较差,所以这就不提django自带的序列化组件了. 首先rest_framework的序列化组件使用同fr ...

  9. 4)drf序列化组件 Serializer(偏底层)、ModelSerializer(重点)、ListModelSerializer(辅助群改)

    知识点:Serializer(偏底层).ModelSerializer(重点).ListModelSerializer(辅助群改) 一.Serializer 偏底层 一般不用 理解原理 1.序列化准备 ...

  10. 揭开DRF序列化技术的神秘面纱

    在RESTful API中,接口返回的是JSON,JSON的内容对应的是数据库中的数据,DRF是通过序列化(Serialization)的技术,把数据模型转换为JSON的,反之,叫做反序列化(dese ...

随机推荐

  1. 2017国家集训队作业[agc016b]Color Hats

    2017国家集训队作业[agc016b]Color Hats 题意: 有\(N\)个人,每个人有一顶帽子.帽子有不同的颜色.现在,每个人都告诉你,他看到的所有其它人的帽子共有多少种颜色,问有没有符合所 ...

  2. es6 学习2 模板字符

    es6模板字符简直是开发者的福音啊,解决了ES5在字符串功能上的痛点. 1.第一个用途,基本的字符串格式化.将表达式嵌入字符串中进行拼接.用${}来界定 //es5 var name = 'lux' ...

  3. navigator.mediaDevices.getUserMedia

    navigator.mediaDevices.getUserMedia: 作用:为用户提供直接连接摄像头.麦克风的硬件设备的接口 语法: navigator.mediaDevices.getUserM ...

  4. 钓鱼WIFI的防范

    实际上,Wi-Fi接入点(AP).路由器和热点常常是高度暴露的攻击面.用户一不小心就有可能踏进攻击者设置的Wi-Fi陷阱,为企业造成信息泄露或经济损失. 如今Wi-Fi 6时代悄然到来,为高密海量无线 ...

  5. TCP 三次握手,四次挥手

    TCP 三次握手,四次挥手 1. TCP 三次握手 建立连接前,客户端和服务端需要通过握手来确认对方: 客户端发送 syn(同步序列编号) 请求,进入 syn_send 状态,等待确认 服务端接收并确 ...

  6. 【Henu ACM Round#19 D】 Points on Line

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 考虑l..r这个区间. 且r是满足a[r]-a[l]<=d的最大的r 如果是第一个找到的区间,则直接累加C(r-l+1,3); ...

  7. Select For update语句浅析

    Select -forupdate语句是我们经常使用手工加锁语句.通常情况下,select语句是不会对数据加锁,妨碍影响其他的DML和DDL操作.同时,在多版本一致读机制的支持下,select语句也不 ...

  8. Ubuntu下安装git工具

    环境:Ubuntu 9.10 git-1.8.2.3.tar.bz2 1.将安装包下载到所选文件夹下,如:/tmp 2.tar -xjf git-1.8.2.3.tar.bz2 3.cd git-1. ...

  9. SQLite: sqlite_master(转)

    转自:http://blog.sina.com.cn/s/blog_6afeac500100yn9k.html SQLite数据库中一个特殊的名叫 SQLITE_MASTER 上执行一个SELECT查 ...

  10. JS 引擎基础之 Shapes and Inline Caches

    阅读下面这篇文章,需要20分钟: 一起了解下 JS 引擎是如何运作的吧! JS 的运作机制可以分为 AST 分析.引擎执行两个步骤: JS 源码通过 parser(分析器)转化为 AST(抽象语法树) ...