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小结