drf篇:序列化类Serializer

序列化类Serializer

  • 简介
  • 序列化类基本使用
  • 序列化类常用字段和字段参数
  • 序列化类Serializer的反序列化
  • 局部和全局钩子
  • 序列化类ModelSerializer的使用
  • 多表序列化
  • 请求与响应

1、serializers简介

# 序列化组件--->一个类,可以完成下面的事
  1. 序列化,把模型对象(book,queryset)转换成字典,经过drf的response以后变成json字符串
  2. 反序列化,把客户端发送过来的数据(前端json格式字符串),经过request.data以后变成字典,序列化器可以把字典转成模型,存到数据库
  3. 反序列化,完成数据校验功能---》前端传入的数据,长短,是否是邮箱,手机号。。可以做校验
  
  
# 1 序列化的功能  表中数据,序列化后,以json格式返回给前端

2、序列化类基本使用

2.1路由

 path('books/', views.BookAPIView.as_view()),

2.2 视图类

from .models import Book
from .serializer import BookSerializer
class BookAPIView(APIView):
    def get(self,request):
        # 取出所有图书的qs对象
        books=Book.objects.all()
        # 借助于序列化类--》写一个序列化类
        # 类实例化时:instance:要序列化的对象   many:True 多条 先记着这两个参数
        ser=BookSerializer(instance=books,many=True)
        # ser.data  完成了序列化
        print(ser.data)  # 把qs对象,转成了字典,根据序列化类中你写的字段转的
        return Response(ser.data)

2.3 序列化类

from rest_framework import serializers

class BookSerializer(serializers.Serializer):  # 一定要继承Serializer
    # 写要序列化的字段,比如,不想把作者给前端
    name = serializers.CharField()  # 字段类,跟models中一一对应
    price = serializers.IntegerField()  # 字段类,跟models中一一对应
    publish = serializers.CharField()  # 字段类,跟models中一一对应
    author = serializers.CharField()  # 字段类,跟models中一一对应

3、序列化类常用字段和字段参数

# 字段类和字段属性

### 字段类:跟models一一对应,但是比它多
# BooleanField 	
# NullBooleanField 	
# CharField 	
# EmailField
# RegexField 
# SlugField 
# URLField 	
# UUIDField 	
# IPAddressField 	
# IntegerField 	
# FloatField 
# DecimalField 
# DateTimeField 	
# DateField 	
# TimeField 	
# ChoiceField 	
# FileField 	
# ImageField 	

------ 以上都是models有的----下面是serializer独有的---
# ListField 	
# DictField 	



#### 字段属性--->写在字段类中的---》干啥用?反序列化校验用的!!!!
## 选项参数
# CharField
max_length 	最大长度
min_lenght 	最小长度
allow_blank 	是否允许为空
trim_whitespace 	是否截断空白字符

# IntegerField
max_value 	最小值
min_value 	最大值

# 通用参数:
### 重点讲
read_only 	表明该字段仅用于序列化输出,默认False
write_only 	表明该字段仅用于反序列化输入,默认False

required 	表明该字段在反序列化时必须输入,默认True
default 	反序列化时使用的默认值
allow_null 	表明该字段是否允许传入None,默认False
error_messages 	包含错误编号与错误信息的字典

4、serializer的反序列化

视图类

from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import BookSerializer
# 图书新增:psot  图书查询所有:get
class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        ser=BookSerializer(instance=book_list,many=True)
        return Response(ser.data)
    def post(self,request):
        # 反序列化---》传得是data=前端传入的数据request.data
        ser=BookSerializer(data=request.data)
        # 数据校验
        if ser.is_valid():  # forms组件就这么做的
            # 保存-->会报错----》需要在序列化类中重写create方法
            ser.save()
            return Response({'msg':'新增成功','code':100})
        else:
            print(ser.errors)
            return Response({'msg': '数据没有校验通过', 'code': 101})
# 图书查询一个:get  ,图书修改一个:put  ,图书删除一个:delete
class BookDetailView(APIView):
    def get(self,request,pk):
        book = Book.objects.all().filter(pk=pk).first()
        ser = BookSerializer(instance=book)
        return Response(ser.data)
    def delete(self,request,pk):
        Book.objects.all().filter(pk=pk).delete()
        return Response({'code':100,'msg':"删除成功"})


    def put(self,request,pk):
        # 修改,就有book对象,拿到要修改的对象
        book=Book.objects.filter(pk=pk).first()
        # 使用data的数据,修改book这个对象
        ser = BookSerializer(instance=book,data=request.data)
        if ser.is_valid():
            # 会报错,需要重写序列化类的updata方法
            ser.save()
            return Response({'code':100,'msg':"修改成功"})
        else:
            return Response({'code': 101, 'msg': "修改失败"})

反序列化类

from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.Serializer):
    # read_only=True  意思是只读,只用来做序列化,不用来做反序列化
    # 如果不写read_only=True,这个字段必传,如果不传,数据校验不过
    # 如果写了read_only=True,这个字段不传
    id=serializers.IntegerField(read_only=True)  # model中Auto,本质就是数字,IntegerField
    name=serializers.CharField(max_length=8,min_length=3,error_messages={'max_length':'太长了'})
    # price=serializers.IntegerField()
    price=serializers.CharField(max_length=1000)  # 写成CharField也能映射成功

    def create(self, validated_data):  # 代码一点没少写,甚至多了,好处解耦了,view代码少了
        # validated_data就是校验过后的数据
        # 高级
        book=Book.objects.create(**validated_data)
        # 菜鸡
        # name=validated_data.get('name')
        # price=validated_data.get('price')
        # book = Book.objects.create(name=name,price=price)
        return book  # 一定要return新增的对象

    def update(self, instance, validated_data):
        # instance 是要修改的对象
        # validated_data是校验过后数据
        instance.name=validated_data.get('name')
        instance.price=validated_data.get('price')
        instance.save()   # 一定不要忘了保存,才存到数据库
        return instance  # 一定要return新增的对象

重点剖析


# 改了put方法和post方法

# 1 在视图类中使用
  # 新增:不需要instance参数
   ser=BookSerializer(data=request.data)
   if ser.is_valid(): 
       ser.save()
  # 修改:需要instance参数
   ser=BookSerializer(instance=book,data=request.data)
   if ser.is_valid(): 
       ser.save()
# 2 在序列化类中,必须重写create和updata,否则报
    def create(self, validated_data):
        book=Book.objects.create(**validated_data)
        return book 

    def update(self, instance, validated_data):
        instance.name=validated_data.get('name')
        instance.price=validated_data.get('price')
        instance.save()   # 一定不要忘了保存,才存到数据库
        return instance  # 一定要return新增的对象
      
      
#### 每个字段有自己的校验规则#####
      
# 数据校验---》序列化类的字段,有字段属性
# 在字段上加属性---》就会按这个属性校验
  name=serializers.CharField(max_length=8,min_length=3)
  price=serializers.CharField(min_length=1000)  # 写成CharField也能映射成功
# 拿到数据校验失败的原因
	ser.errors 

5、局部钩子与全局钩子


# 无论新增还是修改,只要执行ser.is_valid()就会触发校验
# 牢牢记住:先走字段自己的校验规则---》再走局部钩子---》再走全局钩子



# 写在序列化类中的局部钩子函数,校验单个字段
# 写在序列化类中的全局钩子函数,校验所有字段


    # 局部钩子,校验书名不能以sb开头   forms组件没有,validate有
    def validate_name(self,name):
        # name就是  要校验的字段对应的前端传入的值
        if name.startswith('sb'):
            # 校验失败,抛异常
            raise ValidationError('不能以sb开头')
        else:
            return name


    # 全局钩子
    def validate(self, attrs):
        # attrs 校验过后的数据---》字段自己校验完后,局部钩子走完过的数据
        name=attrs.get('name')
        price=attrs.get('price')
        # 加判断,如果通过,就返回attrs
        # 如果不通过就抛异常
        if name == price:
            raise ValidationError('名字和价格不能一样')
        else:
            return attrs

6、序列化类ModelSerializer的使用

重点剖析

# Serializer基类,序列化类都要继承它,写字段,重写create和updata方法

# 有没有一种简单方案,不写字段了(因为字段跟表有对应关系),不写update和create了
# 原来必须要重写update和create了的原因是---》新增不知道要增加到哪个表中去

# ModelSerializer就可以完成---》跟表有对应关系---》字段不用写了,存到哪也知道,不写update和create了


# 重点:
	  1 class Meta:  中model和fields意思
      2 给字段类加属性 extra_kwargs
      3 局部全局钩跟之前没有任何区别
      4 create和update不需要写了
      5 可以重写某个字段,但注意缩进

序列化类的编写

class BookSerializer(serializers.ModelSerializer):
    # 要序列化的字段,没有写,但是写了model=Book,它自动把Book表中的字段映射出来,相当于
    # name = serializers.CharField(max_length=32)
    # price = serializers.IntegerField()

    # 缩进关系搞懂
    class Meta:
        model = Book  # 跟Book做对应
        # fields='__all__' # 序列化和反序列化Book中的所有字段
        fields = ['name', 'price']  # 只序列化和反序列化某几个字段
        # 重点: 如果不重写字段,但是我想给字段类加属性---》使用下面的方式
        # 如果不这样写,name会默认使用model的规则max_length=32
        # 如果这样写了,最大长度是8 ,最小长度是3
        extra_kwargs = {
            'name': {'max_length': 8, 'min_length': 3},
            'price': {'max_value': 88}
        }

    # 表示重写name这个字段,会走它的规则
    # name=serializers.CharField(max_length=8,min_length=3)
    
    
    # 之前教的局部钩子和全局钩子完全一样,一毛一样
    
    # 不需要重写create和update方法了,因为ModelSerializer已经写了

7、多表序列化

图书表,出版社表,作者,作者详情 (图书作者中间表)

7.1 路由

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookView.as_view()),
    path('books/<int:pk>', views.BookDetailView.as_view()),
]

7.2 视图类

from .models import Book,Author,AuthorDetail,Publish
from rest_framework.response import Response
from .serializer import BookSerializer
class BookView(APIView):
    def get(self, request):
        book_list=Book.objects.all()
        #序列化
        ser=BookSerializer(book_list,many=True)
        return Response(ser.data)

    # 反序列化的新增
    def post(self,request):
        ser=BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code':100,'msg':'新增成功'})
        else:
            return Response({'code':101,'msg':'新增失败','err':ser.errors})

class BookDetailView(APIView):
    def get(self, request,pk):
        book=Book.objects.all().filter(pk=pk).first()
        ser=BookSerializer(book)
        return Response(ser.data)

    def put(self,request,pk):
        book = Book.objects.all().filter(pk=pk).first()
        ser = BookSerializer(instance=book,data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code':100,'msg':'修改成功'})
        else:
            return Response({'code':101,'msg':'修改失败','err':ser.errors})

7.3 序列化类


from rest_framework import serializers
# 相对导入---->一个py文件中如果使用相对导入,这个文件不能以脚本运行
from .models import Book
# # 绝对导入
# from app01.models import Book


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model=Book
        # fields="__all__"
        fields=['nid','name','price',    'publish_detail', 'author_list',    'publish','authors']
        # depth   不要用  --->外键关联一层,我建议不要超过3  不可控,比如我们只想要某几个字段
        # depth=1
        extra_kwargs={
            'publish':{'write_only':True},
            'authors': {'write_only': True},
        }

    # 方式一:重写字段+必须配合一个方法,方法返回啥,该字段就是什么--->该字段只能序列化
    publish_detail=serializers.SerializerMethodField(read_only=True)
    def get_publish_detail(self,obj):
        # 就是当前 book对象
        print(obj)
        # return obj.publish.name
        return {'name':obj.publish.name,'city':obj.publish.city}
    # 把所有的作者:作者名字和作者addr
    author_list=serializers.SerializerMethodField(read_only=True)
    def get_author_list(self,obj):
        # 作者有多个,先拿到所有作者,---》正向查询
        author_list=[]
        for author in obj.authors.all():
            author_list.append({'name':author.name,'addr':author.author_detail.addr})
            return author_list

    # 方式二:在表模型models中写方法,在序列化类中写到fields中
class 表名:
    ...
	def publish_detail(self):
        return {'name':self.publish.name,'email':self.publish.email}

    作者详情
    def author_list(self):
        author_list=[]
        for author in self.authors.all():
            author_list.append({'name':author.name,'addr':author.author_detail.addr})
        return author_list


7.4 模型类

from django.db import models

# Create your models here.
class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    # publish_date = models.DateField(null=True)

    #关联关系
    publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)
    authors=models.ManyToManyField(to='Author')  #自动生成中间表
    def __str__(self):
        return self.name

    # def publish_detail(self):
    #     return {'name':self.publish.name,'email':self.publish.email}

    # 作者详情
    # def author_list(self):
    #     author_list=[]
    #     for author in self.authors.all():
    #         author_list.append({'name':author.name,'addr':author.author_detail.addr})
    #     return author_list


class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)

    '''
     on_delete可以选择的请情况
        -models.CASCADE   级联删除
        -models.SET_NULL  关联字段置为空    null=True
        -models.SET_DEFAULT 关联字段设为默认值 default=0
        -models.DO_NOTHING     由于数据库有约束会报错,去掉外键关系(公司都不建立外键)
        -on_delete=models.SET(值,函数内存地址)  设置上某个值,或者运行某个函数
    
    
    '''


class AuthorDetail(models.Model):
    nid = models.AutoField(primary_key=True)
    telephone = models.BigIntegerField()
    birthday = models.DateField()
    addr = models.CharField(max_length=64)


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

8、请求与响应

2.1 请求

# django ---->请求对象----》request
# django ---->响应对象----》render,HttpResponse,Redirect,JsonResponse---》本质都是HttpResponse

# drf中---》请求对象----》新的request---》drf的Request类的对象
# drf的请求对象
	-request.data
    -request.query_params
    -request.FILES
    
# 控制前端传入的编码格式---》默认 :urlencoded,form-data,json
	# 全局生效
	-项目配置文件中加入
        REST_FRAMEWORK = {
            'DEFAULT_PARSER_CLASSES':[
                'rest_framework.parsers.JSONParser',  # 能够解析json
        		'rest_framework.parsers.FormParser',  # 能够解析urlencoded
        		'rest_framework.parsers.MultiPartParser', #能够解析form-data
               ],
        }
    # 局部生效---》视图类中配置
    class BookView(APIView):
    	parser_classes = [JSONParser,]
        
        
   # 优先级:先找 视图类---》项目配置文件----》drf配置文件

8.2 响应

from rest_framework.response import Response

#初始化对象,传入的参数,response对象的属性
    data=None,   # 要序列化的数据,字典,列表,字符串
    status=None, # http响应状态码
    headers=None, # 响应头---》字典
    -------最重要的-----
    template_name=None, #模板名字---》浏览器看的时候,看到好看的页面,postman看,纯json
    content_type=None # 默认json格式,前后端分离项目,前端传用json,后端响应也用json格式
    
    
    
# 浏览器看的时候,看到好看的页面,postman看,纯json----》想控制,都是json格式,无论是浏览器还是postman

	# 全局生效
	-项目配置文件中加入
        REST_FRAMEWORK = {
            'DEFAULT_RENDERER_CLASSES':[
          		'rest_framework.renderers.JSONRenderer',   #json格式
        		'rest_framework.renderers.BrowsableAPIRenderer', #浏览器格式
               ],
        }
    # 局部生效---》视图类中配置
    class BookView(APIView):
    	renderer_classes = [JSONRenderer,BrowsableAPIRenderer]
        
        
   # 优先级:先找 视图类---》项目配置文件----》drf配置文件

细节补充

(1) 相对导入和绝对导入

# 绝对导入---》绝对指的是 环境变量   sys.path 里面的路径,项目跟路径在pycharm自动加入环境变量
# 相对导入---->一个py文件中如果使用相对导入,这个文件不能以脚本运行

(2) django国际化配置

# django 国际化---》配置文件改

# 国际化相关
LANGUAGE_CODE = 'zh-hans'
#时区:改成东八区
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False

(3) models 的级联删除相关

    '''
     on_delete可以选择的请情况
        -models.CASCADE   级联删除
        -models.SET_NULL  关联字段置为空    null=True
        -models.SET_DEFAULT 关联字段设为默认值 default=0
        -models.DO_NOTHING     由于数据库有约束会报错,去掉外键关系(公司都不建立外键)
        -on_delete=models.SET(值,函数内存地址)  设置上某个值,或者运行某个函数
    '''

(4) 原生django,如何向响应头写数据

def test(request):
    obj=HttpResponse('ok')
    # obj=render()
    # obj=redirect('/index')
    # obj=JsonResponse()
    obj['xxx']='ooo'  #直接放
    return obj
posted @ 2022-06-15 17:57  马氵寿  阅读(106)  评论(0)    收藏  举报