DRF:源码剖析 - 序列化组件(serializer)
简介
序列化主要是指,能够将 queryset 类型进行序列化,因为 json 转化无法直接将 queryset 序列化,才有了序列化组件。
django rest framework 中的序列化组件,可以说是其核心组件,也是我们平时使用最多的组件,它不仅仅有序列化功能,更提供了数据验证的功能(与django中的form类似)。
便于展现的序列化操作,我们需要在model添加外键、多对多情况。以下是新的models(请删除原有的数据库,重新migrate):
以图书管理系统为例(首先建立models):
# models.py
from django.db import models # Create your models here. class Author(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) age = models.IntegerField() def __str__(self): return self.name 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 Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) price = models.DecimalField(max_digits=5, decimal_places=2) # 外键字段(一对多关系) publish = models.ForeignKey(to="Publish", related_name="book", related_query_name="book_query", on_delete=models.CASCADE) # 多对多字段 authors = models.ManyToManyField(to="Author")
进行数据库的迁移
python manage.py makemigrations
python manage.py migrate
Django原生的序列化
from django.core.serializers import serialize from django.views import View class CourseView(View): def get(self,request): # 获取queryset orign_data = Book.objects.all() # 开始序列化 serialized_data = serialize('json',orign_data) return HttpResponse(serialized_data)
注意:对queryset数据类型进行序列化
rest_framework序列化组件基本使用及接口设计
一、接口设计的基本规范
# 查询
GET 127.0.0.1:8000/books/ # 获取所有数据,返回值: [{}, {}] GET 127.0.0.1:8000/books/{id} # 获取一条数据,返回值:{}
# 添加 POST 127.0.0.1:8000/books/ # 新增一条数据,返回值:{}
# 修改 PUT 127.0.0.1:8000/books/{id} # 修改数据,返回值:{}
# 删除 DELETE 127.0.0.1:8000/books/{id} # 删除数据,返回空
二、序列化组件使用的基本步骤
(1)导入模块
(2)建立一个序列化类
(3)获取queryset
(4)开始序列化
(5)获取序列化后的数据,返回给客户端
三、序列化组件基本使用(serializer)
以查看数据库中所有数据为例(GET请求)
(1)urls.py
设计,这里使用了url 的分发,添加了查询书籍的 url
# drfserver / url.py (项目中)
from django.contrib import admin from django.urls import path, include, re_path urlpatterns = [ path('admin/', admin.site.urls), re_path('serializer/', include('serializer.urls')) ]
# serializer(app) / urls.py
from django.urls import re_path from serializer import views urlpatterns = [ re_path(r'books/$', views.BookView.as_view()), ]
(2)views.py
from rest_framework.views import APIView from rest_framework.response import Response from .models import ( Book, Author, ) # Create your views here. from rest_framework import serializers # 创建一个序列化类,字段类型不一定要跟models的字段一致 class BookSerializer(serializers.Serializer): # nid = serializers.CharField(max_length=32) title = serializers.CharField(max_length=128) price = serializers.DecimalField(max_digits=5, decimal_places=2) publish = serializers.CharField(max_length=32, source="publish.city") #仅序列化publishd的city字段 authors = serializers.CharField(max_length=32) # book_obj.authors.all()
# 视图函数处理 class BookView(APIView): def get(self, request): origin_data = Book.objects.all() # 获取queryset serialized_data = BookSerializer(instance=origin_data, many=True) #开始序列化,序列化的结果存储在 .data属性中,多条数据,many=True, 单条数据 many=False
#instance=... 既可以是queryset对象,也可以是一个对象
return Response(serialized_data.data)
访问:
结果:
分析:
publish 和 authors 有外键和多对多的关系,因为publish中有,source=publish.city,所以出来的是城市,但是authors是多对多的关系,在表中不知道要序列化哪些字段,基本使用并不能满足需求,因此需要我们自定义序列化字段
四、自定义序列化字段(get、post接口设计)
由于获取所有数据、添加数据不需要进行传参,可以走同一个视图函数,BookView
当数据模型中有外键或者多对多时候,这时候就需要自定序列化了,(ModelSerializer)
以在数据库中增加一条数据为例:(POST请求)
(1)urls.py
设计,这里使用了url 的分发,添加了查询书籍的 url
不涉及到对某条数据的修改,不需要传id,因此仍然可以使用查询所有数据的url.
# drfserver / url.py (项目中)
from django.contrib import admin from django.urls import path, include, re_path urlpatterns = [ path('admin/', admin.site.urls), re_path('serializer/', include('serializer.urls')) ]
# serializer(app) / urls.py
from django.urls import re_path from serializer import views urlpatterns = [ re_path(r'books/$', views.BookView.as_view()), ]
(2)views.py
存在的问题:
a、 serializers.Serializer无法插入数据,只能自己实现create
b、 字段太多,不能自动序列化,
因此使用 serializers.ModelSerializer
from rest_framework.views import APIView from rest_framework.response import Response from .models import ( Book, Publish, Author, ) from rest_framework import serializers # ----------------------- 创建序列化类 ♥♥♥ -----------------------
class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = ('title', 'price', 'publish', 'authors', 'author_list', 'publish_name', 'publish_city' ) # 返回给前端,显示的字段 extra_kwargs = { 'publish': {'write_only': True}, 'authors': {'write_only': True} } # 该字段只可写,不可读,不能够返回给前端 publish_name = serializers.CharField(max_length=32, read_only=True, source='publish.name') # 在field中,必须声明出该字段publish_name,否则会报错 publish_city = serializers.CharField(max_length=32, read_only=True, source='publish.city') author_list = serializers.SerializerMethodField() # 多对多的关系,需要SerializerMethodField,并且用"get_字段名"方法,取值,在field中需要声明 def get_author_list(self, book_obj): # 拿到queryset开始循环 [{}, {}, {}, {}] authors = list() for author in book_obj.authors.all(): authors.append(author.name) return authors
# --------------------------- 视图函数 ---------------------------
class BookView(APIView):
# 1、查询所有数据(get请求) def get(self, request): origin_data = Book.objects.all() # 获取queryset serialized_data = BookSerializer(origin_data, many=True) #开始序列化,序列化的结果存储在 .data属性中 return Response(serialized_data.data)
# 2、添加数据(post请求) def post(self, request): verified_data = BookSerializer(data=request.data) # 进行字段校验,request.data 使用APIView,接收的可以是json数据 if verified_data.is_valid(): # 验证数据的正确性 book = verified_data.save() authors = Author.objects.filter(nid__in=request.data['authors']) # 字段 authors=[1,2] book.authors.add(*authors) return Response(verified_data.data) else: return Response(verified_data.errors)
1)get 请求(获取所有数据)
发送请求:
返回结果:
2)post 请求(添加数据)
发送请求:
返回结果:
五、其他接口设计(put 、delete、get)
由于单条数据查询、修改某条数据、删除某条数据,涉及到了 记录的 id 值,需要传入参数,因此属于一类,同走一个视图函数,BookFilterView.
(1)urls.py 采用了分发的形式
urlpatterns = [ path('admin/', admin.site.urls), path('serializer/', include('serializer.urls')), ]
from django.urls import path,include,re_path from serializer import views urlpatterns = [ re_path(r'books/$',views.BookView.as_view()), re_path(r'books/(?P<pk>\d+)/$', views.BookFilterView.as_view()), # url中一定要指明pk(命名路由) ]
(2)views.py
创建的序列化类同上面保持一致,下面是具体的视图函数:
class BookFilterView(APIView): # 3、获取单条数据(get请求) def get(self, request, nid): book_obj = Book.objects.get(pk=nid) serialized_data = BookSerializer(book_obj, many=False) return Response(serialized_data.data) # 4、修改某条数据(put请求) def put(self, request, nid): book_obj = Book.objects.get(pk=nid) verified_data = BookSerializer(data=request.data, instance=book_obj) # inatance参数 既可以是对象,也可以是queryset if verified_data.is_valid(): verified_data.save() return Response(verified_data.data) else: return Response(verified_data.errors) # 5、删除某条数据(delete) def delete(self, request, nid): book_obj = Book.objects.get(pk=nid).delete() return Response()
序列化字段的扩展
一、
源码剖析