源码分析+序列化类Serializer

CBV源码分析

在视图层,有FBV和CBV之分,CBV即是基于类的视图,必须要继承一个View类。

from django.views import View

流程分析

# 首先从路由开始
path('books/', views.BookViwe.as_view())

# views.BookViwe.as_view()的执行结果,是函数内存地址>>>>>>>>>>view这个函数的内存地址
# 即请求通过路由匹配成功后,就会执行def view(request),(*args, **kwargs)为有名无名分组传入的参数
def view(request, *args, **kwargs):
    return self.dispatch(request, *args, **kwargs)

# def view(request)返回一个self.dispatch(request)
# .dispatch(request) 你是什么请求,就会执行视图类中请求名字的方法
def dispatch(self, request, *args, **kwargs):
    if request.method.lower() in self.http_method_names:
    '''
    请求方法变成小写字符串,如果是get请求,就是 'get'
    self.http_method_names是一个列表 ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    '''
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        '''
        重点:反射,去对象中,根据字符串,取出方法或属性
        'get'是请求方法的字符串----> 反射(self是视图类的对象) ----> handler就是你写在视图类中的get方法
        '''
    else:
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)
	'''
	执行视图函数的get方法
	'''

CBV原理:你是什么请求,就会执行视图类中请求名字的方法

APIView的基本使用

如果使用了drf,以后写的都是cbv,但是都是继承drf提供的APIView类

from rest_framework.views import APIView

drf是个app,需要注册

INSTALLED_APPS = [
		...
    'rest_framework'   # 一定不要忘了注册
]

# 在注册时,每一个app后面都要加逗号,并且不能使用 '' 或 '''''' 来注释,因为INSTALLED_APPS是一个列表

实例:

class BookViwe(APIView):
    def get(self,request):
        # return HttpResponse('hello web')
        return Response('hello web') 
    '''
    以后不要用django原生的响应了:render,HttpResponse,Redirect,JsonResponse
    drf提供的响应:Response(放字符串,字典,列表:HttpResponse+JsonResponse结合体)
    '''
 
# 并且由于继承了APIView ----> 执行APIView的as_view ----> 执行APIView的dispatch(内部执行变化了)

APIView执行流程分析

于CBV相比,路由写法没变,执行发生了变化

# 首先从路由开始
path('books/', views.BookViwe.as_view())

# views.BookViwe.as_view()的执行结果,是函数内存地址 ----> view这个函数的内存地址
# 即请求通过路由匹配成功后,就会执行def view(request),(*args, **kwargs)为有名无名分组传入的参数
# 现在的as_view已经不是django原生View的as_view了,而是drf的APIView的as_view了
def as_view(cls, **initkwargs):
    view = super().as_view(**initkwargs)
    '''
    这个view就是原生django中View类的view,还是原来的view
    '''
    return csrf_exempt(view)
    '''
    csrf_exempt()可以去除去除了所有的csrf,之前post请求,如果不注释掉csrf中间件,就会报错
    使用csrf_exempt装饰器装饰视图函数,其本质跟csrf_exempt(view)是一样的:即把被装饰的函数当做参数,传入装饰器函数,返回值赋值给被装饰的函数
    重点:1.view还是原来的view 
    	 2.以后不用管csrf了
    '''
    
# 因为view还是原来的view,返回一个self.dispatch(request)
# 但dispatch已经不是django原生View的dispatch了,是drf的APIView的dispatch了
def dispatch(self, request, *args, **kwargs):
    request = self.initialize_request(request, *args, **kwargs)
    '''
    包装了一个新的request对象
    '''
    try:
    '''
    捕获了异常
    内部执行了三大认证:认证(登陆认证),频率(1分钟只能访问接口3次),权限(普通用户不能访问,超级用户才能访问)
    '''
        self.initial(request, *args, **kwargs)
        '''
        原来dispatch的代码,重写了一下---》执行视图函数
        '''
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(),
                              self.http_method_not_allowed)
        else:
        	handler = self.http_method_not_allowed
        response = handler(request, *args, **kwargs)
        '''
        执行对应请求方法的视图函数
        '''
    except Exception as exc:
        '''
        处理了异常,统一异常处理 ----> 返回固定格式
        '''
        response = self.handle_exception(exc)
        '''
        包装了response对象,浏览器看到好看的样式,postman只看到json数据
        '''
    self.response = self.finalize_response(request, response, *args, **kwargs)
    return self.response

# 重点
1.drf:包装了新的request对象(drf的Request的对象)
2.在执行视图函数之前,执行了三大认证
3.处理了全局异常,执行三大认证期间,执行视图函数期间,如果出了错,都能统一处理
4.包装了响应对象

Request对象源码分析

原生:rest_framework.request.Request

drf:django.core.handlers.wsgi.WSGIRequest

class Request:
	__init__:    self._request = request(这个request是老的)
	'''
	把老的request放到了新的request的_request属性了
	'''         
		def __getattr__(self, attr):
		'''
         新的request用起来跟原来一样用
         因为重写了__getattr__ 方法,对象.属性 如果属性不存在,触发这个方法执行
         而新的request中没有POST,在使用request.POST时,触发Request类的__getattr__ 
         '''   
		try:
			return getattr(self._request, attr)
		except AttributeError:
			return self.__getattribute__(attr)
		'''
		通过反射,去拿老request的属性或方法
		'''
        
# 新的request有个data方法,包装成了数据属性
-前端无论什么编码方式传得body体中的数据,都从request.data中取,当字典用即可
-原生djagno,如果是formdata,urlencoded是从POST中取,如果是json,从body中取出来自己转
        
# 新的request有个query_params方法,包装成了数据属性
-就是原来老的request的GET属性
      
# 上传文件,跟之前一样用,但是人家重写了

# 重点
1.老的request是新的request._request
2.跟原来一样用
3.body提交的数据都从request.data中取
4.请求地址中的数据都从request.query_params中取

序列化类Serializer的使用

Serializer类,可以实现以下功能

1. 序列化,把模型对象(book,queryset)转换成字典,经过drf的response以后变成json字符串
2. 反序列化,把客户端发送过来的数据(前端json格式字符串),经过request.data以后变成字典,序列化器可以把字典转成模型,存到数据库
3. 反序列化,完成数据校验功能,前端传入的数据,长短,是否是邮箱,手机号......可以做校对

路由层

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

视图层

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)

序列化类

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中一一对应

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

字段类

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 	包含错误编号与错误信息的字典

使用APIView+序列化类+Response来写5个接口

序列化:查单个,查所有
反序列化:新增一个,修改一个

序列化类

from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.Serializer):
    id=serializers.IntegerField(read_only=True)
    # read_only=True  意思是只读,只用来做序列化,不用来做反序列化
    # 如果不写read_only=True,这个字段必传,如果不传,数据校验不过
    # 如果写了read_only=True,这个字段不传
    # model中Auto,本质就是数字,IntegerField
    name=serializers.CharField(max_length=8,min_length=3,error_messages={'max_length':'太长了'})
    price=serializers.CharField(max_length=1000)  
    # 写成CharField也能映射成功
    
    
# 重写的 create() update() 方法
	def create(self, validated_data):
    '''
    is_valid()方法,做了两件事,一个把验证过的数据赋值给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.name=validated_data.get('name')
        instance.price=validated_data.get('price')
        '''
        instance 是要修改的对象
        validated_data是校验过后数据
        '''
        instance.save()   # 一定不要忘了保存,才存到数据库
        return instance  # 一定要return新增的对象

视图类

from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import BookSerializer
from .models import Book
# 图书新增: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组件就这么做的
        '''
        is_valid()方法,做了两件事,一个把验证过的数据赋值给validated_data属性,一个返回布尔值判断数据是否验证通过
        '''
            # 保存到数据库 ----> 会报错 ----> 所以在序列化类中重写了create方法
            ser.save()
            return Response({'msg':'新增成功','code':100})
        else:
            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': "修改失败"})

总结

# 如果需要使用数据验证,在视图类中使用
  # 增
   ser=BookSerializer(data=request.data)
   if ser.is_valid(): 
       ser.save()
  # 改
   ser=BookSerializer(instance=book,data=request.data)
   if ser.is_valid(): 
       ser.save()
        
# 需要使用.save()保存到数据库,在序列化类中,重写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 

局部和全局钩子

1.如果需要对字段值的内容进行校验,参照forms组件,使用钩子函数
2.无论新增还是修改,只要执行ser.is_valid()就会触发校验
3.校验顺序 字段自己的校验规则 ----> 再走局部钩子 ----> 再走全局钩子

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


# 局部钩子,校验书名不能以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

模块序列化类ModelSerializer的使用

Serializer基类,序列化类都要继承它。

在使用序列化类Serializer时,需要写字段对应,并且需要重写create和updata方法,有没有一种简单方案,不写字段了(因为字段跟表有对应关系),不重写update和create了,原来必须要重写update和create的原因是新增不知道要增加到哪个表中去。

使用ModelSerializer就可以不需要以上步骤。

# 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已经写了

多表序列化

与单表序列化相比,路由与视图类不变,序列化类和模型类发生变化

方式一:在序列化类中写方法

方式二:在模型类中写方法

路由

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

视图类

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})

序列化类

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model=Book
        # fields="__all__"
        fields=['nid','name','price',    'publish_detail', 'author_list',    'publish','authors']
        # 设置字段为只写
        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

模型类

from django.db import models

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

    # 方式二:在表模型models中写publish_detail方法,在序列化类中写到fields中  
    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

请求与响应

请求

django中请求对象:request

drf中请求对象:drf的Request类的对象

# drf的请求对象
	-request.data
    -request.query_params
    -request.FILES

控制前端传入的编码格式

# 全局生效
-项目配置文件中加入
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配置文件

响应

django中响应对象:render,HttpResponse,Redirect,JsonResponse(本质都是HttpResponse)

drf中响应对象:Response

from rest_framework.response import Response

data=None,   # 要序列化的数据,字典,列表,字符串 # 初始化对象传入的参数,response对象的属性
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配置文件
posted @ 2022-06-14 22:11  Rain_Kz  阅读(34)  评论(0编辑  收藏  举报