Title(EN): Django REST Framework Learning Notes (8): the ModelSerializer class in serialziers module
Author: dog2
基本信息
源码:rest_framework.serializers
官方文档
API Guide - Serializers
API Guide - Serializer fields
API Guide - Serializer relations
本文demo代码Github
DRF常用序列化类主要有
rest_framework.serialziers.Serializer
rest_framework.serialziers.ModelSerializer
rest_framework.serialziers.ListSerializer
本篇介绍rest_framework.serialziers.ModelSerializer
,将结合图书管理系统实例说明。
序列化以外的其他配置
settings.py
1 2 3 4 5 6 7 8 9 10 11 12 13 INSTALLED_APPS = [ 'rest_framework' , ] LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True USE_TZ = False MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media' )
全局url.py
1 2 3 4 5 6 7 8 9 from django.contrib import adminfrom django.urls import path, includefrom django.conf import settingsfrom django.conf.urls.static import staticurlpatterns = [ path('admin/' , admin.site.urls), path('api/' , include('api.urls' )), ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
数据库表
表一览
BaseModel
基表:is_delete
、create_time
下面四表继承基表,可以继承两个字段
Book
表:name
、price
、img
、authors
、publish
、is_delete
、create_time
Publish
表:name
、address
、is_delete
、create_time
Author表
:name
、age
、is_delete
、create_time
AuthorDetail
表:mobile, author
、is_delete
、create_time
基表代码
models.py
1 2 3 4 5 6 7 class BaseModel (models.Model) : is_delete = models.BooleanField(default=False ) create_time = models.DateTimeField(auto_now_add=True ) class Meta : abstract = True
断关联多表关系(重点)
外键位置:
一对多:外键放多的一方
一对一:从逻辑正反向考虑,如作者表与作者详情表,作者删除级联作者详情也删除,详情删除作者依旧存在,所以建议外键在详情表中(和之前的一对一关系外键放置不一样,之前都是把外键放在查询频率高的表)
多对多:外键在关系表中
ORM正向方向连表查找:(注:依赖代码见下方)
正向:通过外键字段 eg: author_detial_obj.author
,外键设置在作者详情表,在作者详情表中查询作者直接.author
就可以
反向:通过设置反向查询related_name
的值 eg:author_obj.detail
,外键没有设置在作者表中,在作者表中通过设置反向查询.detail
查询作者详情
连表操作关系:(注:拿作者与作者详情表举例,外键建在作者详情表中)
作者删除,详情级联 1 on_delete=models.CASCADE
作者删除,详情置空 1 null=True , on_delete=models.SET_NULL
作者删除,详情重置 1 default=0 , on_delete=models.SET_DEFAULT
作者删除,详情不动 1 on_delete=models.DO_NOTHING
外键关联字段的参数:如何实现断关联、目前表间操作关系、方向查询字段
作者详情表中的(外键设置在作者详情表中) 1 2 3 4 5 6 author = models.OneToOneField( to='Author' , related_name='detail' , db_constraint=False , on_delete=models.CASCADE )
图书表中的 1 2 3 4 5 6 7 8 9 10 11 publish = models.ForeignKey( to='Publish' , related_name='books' , db_constraint=False , on_delete=models.DO_NOTHING, ) authors = models.ManyToManyField( to='Author' related_name='books' , db_constraint=False , )
注意:
ManyToManyField
不能设置on_delete
OneToOneField
、ForeignKey
必须设置on_delete
(django1.x系统默认级联,但是django2.x必须手动明确)
各表代码
models.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 from django.db import modelsclass BaseModel (models.Model) : is_delete = models.BooleanField(default=False ) create_time = models.DateTimeField(auto_now_add=True ) class Meta : abstract = True class Book (BaseModel) : name = models.CharField(max_length=64 ) price = models.DecimalField(max_digits=5 , decimal_places=2 ) img = models.ImageField(upload_to='img' , default='default.jpg' ) publish = models.ForeignKey( to='Publish' , db_constraint=False , related_name='books' , on_delete=models.DO_NOTHING, ) authors = models.ManyToManyField( to='Author' , db_constraint=False , related_name='books' , ) @property def publish_name (self) : return self.publish.name @property def author_list (self) : return self.authors.values('name' , 'age' , 'detail__mobile' ).all() class Meta : db_table = 'book' verbose_name = '书籍' verbose_name_plural = verbose_name unique_together = ('name' , 'publish' ) def __str__ (self) : return self.name class Publish (BaseModel) : """name、address、is_delete、create_time""" name = models.CharField(max_length=64 ) address = models.CharField(max_length=64 ) class Meta : db_table = 'publish' verbose_name = '出版社' verbose_name_plural = verbose_name def __str__ (self) : return self.name class Author (BaseModel) : """name、age、is_delete、create_time""" name = models.CharField(max_length=64 ) age = models.IntegerField() class Meta : db_table = 'author' verbose_name = '作者' verbose_name_plural = verbose_name def __str__ (self) : return self.name class AuthorDetail (BaseModel) : """mobile, author、is_delete、create_time""" mobile = models.CharField(max_length=11 ) author = models.OneToOneField( to='Author' , db_constraint=False , related_name='detail' , on_delete=models.CASCADE ) class Meta : db_table = 'author_detail' verbose_name = '作者详情' verbose_name_plural = verbose_name def __str__ (self) : return self.author.name
serializers.ModelSerializer
序列化 & 反序列化
序列化
ModelSerializer
模型类序列化器与常规的Serializer
相同,但提供了:
基于模型类自动生成一系列字段
包含默认的create()
和update()
的实现
基于模型类自动为Serializer
生成validators
,比如unique_together
序列化层 api/serializers.py
Source Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 from rest_framework.serializers import ModelSerializer, SerializerMethodFieldfrom rest_framework.exceptions import ValidationErrorfrom . import modelsclass PublishModelSerializer (ModelSerializer) : class Meta : model = models.Publish fields = ('name' , 'address' ) class BookModelSerializer (ModelSerializer) : publish = PublishModelSerializer() class Meta : model = models.Book fields = ('name' , 'price' , 'img' , 'author_list' , 'publish' )
视图层 views.py
Source Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom . import models,serializersclass Book (APIView) : def get (self, request, *args, **kwargs) : pk = kwargs.get('pk' ) if pk: try : book_obj = models.Book.objects.get(pk=pk, is_delete=False ) book_data = serializers.BookModelSerializer( book_obj).data except Exception as err: return Response({ 'status' : 1 , 'msg' : '数据不存在' }) else : book_query = models.Book.objects.filter( is_delete=False ).all() book_data = serializers.BookModelSerializer( book_query, many=True ).data return Response({ 'status' : 0 , 'msg' : 'ok' , 'results' : book_data })
子路由层 api/urls.py
1 2 3 4 urlpatterns = [ path('v1/books/' , views.Book.as_view()), path('v1/books/<pk>/' , views.Book.as_view()), ]
反序列化
序列化层 api/serializers.py
Source Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class BookModelDeserializer (ModelSerializer) : re_name = CharField(read_only=True ) class Meta : model = models.Book exclude = ['is_delete' , 'create_time' , 'img' ] extra_kwargs = { 'name' : { 'required' : True , 'min_length' : 1 , 'error_messages' : { 'required' : '必填项' , 'min_length' : '太短' , }, }, } def validate_name (self, value) : if 'g' in value.lower(): raise ValidationError('该g书不能出版' ) return value def validate (self, attrs) : publish = attrs.get('publish' ) name = attrs.get('name' ) if models.Book.objects.filter(name=name, publish=publish): raise ValidationError({'book' : '该书已存在' }) return attrs
视图层 views.py
1 2 3 4 5 6 7 8 9 10 11 12 class Book (APIView) : def post (self, request, *args, **kwargs) : request_data = request.data book_ser = serializers.BookModelDeserializer(data=request_data) book_ser.is_valid(raise_exception=True ) book_obj = book_ser.save() return Response({ 'status' : 0 , 'msg' : 'ok' , 'results' : serializers.BookModelSerializer(book_obj).data })
子路由层 api/urls.py
1 2 3 4 urlpatterns = [ path('v1/books/' , views.Book.as_view()), path('v1/books/<pk>/' , views.Book.as_view()), ]
注意点
反序列化层:
在设置fields
,没有默认值的字段都必须设置反序列化保存到数据库中
使用extra_kwargs
来设置系统校验规则
设置局部钩子和全局钩子
ModelSerializer
类已经帮我们实现了 create
与 update
方法,不需要再写
视图层:
POST请求通过request.data
拿到数据包
传给反序列化 ,通过data=request_data
传入需要反序列化的数据
is_valid
判断校验是否合格 ,raise_exception=True
必须要写的
通过.save()
保存到数据库中
序列化与反序列化整合
序列化层 api/serializers.py
Source Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class V2BookModelSerializer (ModelSerializer) : re_name = CharField(read_only=True ) class Meta : model = models.Book fields = ('name' , 'price' , 'img' , 'author_list' , 'publish_name' , 'publish' , 'authors' , 're_name' ) extra_kwargs = { 'name' : { 'required' : True , 'min_length' : 1 , 'error_messages' : { 'required' : '必填项' , 'min_length' : '太短' , } }, 'publish' : { 'write_only' : True }, 'authors' : { 'write_only' : True }, 'img' : { 'read_only' : True , }, 'author_list' : { 'read_only' : True , }, 'publish_name' : { 'read_only' : True , } } def validate_name (self, value) : if 'g' in value.lower(): raise ValidationError('该g书不能出版' ) return value def validate (self, attrs) : publish = attrs.get('publish' ) name = attrs.get('name' ) return attrs
注意点
fields
中设置所有序列化与反序列化字段
extra_kwargs
划分只序列化或只反序列化字段(一般我们把需要存入到数据库中的使用write_only
(反序列化),只需要展示的就read_only(序列化),看需求设计)
write_only
:只反序列化
read_only
:只序列化
自定义字段默认只序列化(read_only
)
如果字段没设置write_only
或者read_only
,那么该字段可以序列化和反序列化
设置反序列化所需的 系统、局部钩子、全局钩子 等校验规则
视图层 views.py
Source Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 class V2Book (APIView) : def get (self, request, *args, **kwargs) : pk = kwargs.get('pk' ) if pk: try : book_obj = models.Book.objects.get(pk=pk, is_delete=False ) book_data = serializers.V2BookModelSerializer( book_obj).data except : return Response({ 'status' : 1 , 'msg' : '参数有误' }) else : book_query = models.Book.objects.filter(is_delete=False ).all() book_data = serializers.V2BookModelSerializer( book_query, many=True ).data return Response({ 'status' : 0 , 'msg' : 'ok' , 'results' : book_data }) def post (self, request, *args, **kwargs) : request_data = request.data if isinstance(request_data, dict): many = False elif isinstance(request_data, list): many = True else : return Response({ 'status' : 1 , 'msg' : '数据错误' }) book_ser = serializers.V2BookModelSerializer( data=request_data, many=many) book_ser.is_valid(raise_exception=True ) book_result = book_ser.save() return Response({ 'status' : 0 , 'msg' : 'ok' , 'results' : serializers.V2BookModelSerializer(book_result, many=many).data }) def delete (self, request, *args, **kwargs) : pk = kwargs.get('pk' ) if pk: pks = [pk] else : pks = request.data.get('pks' ) if models.Book.objects.filter(pk__in=pks, is_delete=False ).update(is_delete=True ): return Response({ 'status' : 0 , 'msg' : '删除成功' }) return Response({ 'status' : 1 , 'msg' : '删除失败' })
注意点
序列化数据最后必须要.data
(因为要传给前端)
反序列化通过data
传参,序列化通过instance
传参(当你只传一个参数时,默认是instance
的参数)
反序列化与序列化都能使用many=True
,序列化和反序列化数据只要被[]嵌套都要写many=True
子路由层 api/urls.py
1 2 3 4 urlpatterns = [ path('v2/books/' , views.V2Book.as_view()), path('v2/books/<pk>/' , views.V2Book.as_view()), ]
使用场景总结
序列化类初始化时,instance
/data
/many
/partial
是影响序列化对象行为的四个关键参数。
序列化角度
序列化
如果没有data
参数,只有instance
,那么就不存在反序列化校验一说,只有序列化对象instance
。
1 2 ser_obj = ModelSerializer(model_obj)
反序列化
如果有data
,没有instance
,则需要调用is_valid
方法校验data
,然后将data
进行反序列化,得到validated_data
,此时再通过序列化对象获取data
,这个data
和初始化提供的data
不同,是序列化validated_data
后的data
,比起初始化data
,可能减少了无效的字段(序列化没有定义的字段)。
1 2 ser_obj = ModelSerializer(data=model_data)
数据操作角度(增删改查)
修改场景
如果同时提供了instance
及data
, 那么只要有data
或者部分data
,data
都要进行验证才能进行下面的save
等操作,如果不经过is_valid
过程,那么后面的获取序列化数据或者反序列化数据都会无效。
要用instance
指明要修改的对象partial
用于部分更新,之所以要伴随instance
,是因为要指明给save
用,在save
操作时给那个instance
部分更新。逻辑这回走到下面源码中的get_initial()
获取要进行更新instance
的字段数据。
单改
单整体改,说明前台要提供修改的数据,修改之后保存的数据需要校验,校验的数据应该在实例化“序列化类对象”时,赋值给data
修改,就必须明确被修改的模型类对象,并在实例化“序列化类对象”时,赋值给instance
,必须赋值给instance
整体修改,所有校验规则有required=True
的字段,都必须提供,因为在实例化“序列化类对象”时,参数partial
默认为False
整体改(PUT
)
1 2 3 4 5 V2BookModelSerializer( instance=model_obj, data=model_data, partial=False , )
局部改(PATCH
)
1 2 3 4 5 6 V2BookModelSerializer( instance=model_obj, data=model_data, partial=True , )
群改 ListSerializer
many
参数将直接影响序列化类的类型,如果是many=False
,那么直接使用当前序列化类。如果many=True
,将实例化一个ListSerializer
类来序列化或者反序列化类。
在前面的代码中,我们在V2BookModelSerializer
类中并没有做对多对象序列化的特殊处理,但V2版本的api仍然支持群增和群删操作。究其原因,是由于BaseSerializer.__new__
方法)或者class Meta:
中定义了list_serializer_class
指定的多对象序列化类。
关于 ListSerializer
的详细介绍请看下一篇。
给序列化类传入自定义数据
视图类views.py
使用context
传递参数给序列化类serializers.py
方法
在视图类中实例化序列化对象时,可以设置context
内容
在序列化类中的局部钩子
、全局钩子
、create
、update
方法中,都可以用self.context
访问视图类传递过来的内容。
使用场景
例如有类似如下需求时:
在视图类views
中,可以通过request
得到登陆用户request.user
在序列化类中,要完成数据库数据的校验与入库操作,可能会需要知道当前的登陆用户,但序列化类默认无法访问request
因此在视图类中实例化序列化对象时,将request
对象传递进去
示例代码
视图层 views.py
1 2 3 4 5 6 7 8 9 10 class Book (APIView) : def post (self, request, *args, **kwargs) : book_ser = serializers.BookModelSerializer(data=request_data,context={'request' :request}) book_ser.is_valid(raise_exception=True ) book_result = book_ser.save() return Response({ 'status' : 0 , 'msg' : 'ok' , 'results' : serializers.BookModelSerializer(book_result).data })
序列化层 serializers.py
1 2 3 4 5 6 7 class BookModelSerializer (ModelSerializer) : class Meta : model = models.Book fields = ('name' , 'price' ) def validate_name (self, value) : print(self.context.get('request' ).method) return value
扩展阅读
django rest framework serializers小结