参考博客
[置顶]Python系列教程 | Justin-刘清政的博客
app01/ser.py代码
from django.db import models
# Create your models here.
class Book(models.Model):
id=models.AutoField(primary_key=True)
name=models.CharField(max_length=32)
price=models.DecimalField(max_digits=5,decimal_places=2)
author=models.CharField(max_length=32)
publish=models.CharField(max_length=32)
# from rest_framework.serializers import Serializer # 就是一个类
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
# 需要继承 Serializer
from app01.models import Book
def check_author(data):
if data.startswith('sb'):
raise ValidationError('作者名字不能以sb开头')
else:
return data
class BookSerializer(serializers.Serializer):
id=serializers.CharField(read_only=True)
name=serializers.CharField(max_length=16,min_length=4)
# price=serializers.DecimalField()
price=serializers.CharField(write_only=True,required=True)
author=serializers.CharField(validators=[check_author]) # validators=[] 列表中写函数内存地址
publish=serializers.CharField()
def validate_price(self, data): # validate_字段名 接收一个参数
#如果价格小于10,就校验不通过
# print(type(data))
# print(data)
if float(data)>10:
return data
else:
#校验失败,抛异常
raise ValidationError('价格太低')
def validate(self, validate_data): # 全局钩子
print(validate_data)
author=validate_data.get('author')
publish=validate_data.get('publish')
if author == publish:
raise ValidationError('作者名字跟出版社一样')
else:
return validate_data
def update(self, instance, validated_data):
#instance是book这个对象
#validated_data是校验后的数据
instance.name=validated_data.get('name')
instance.price=validated_data.get('price')
instance.author=validated_data.get('author')
instance.publish=validated_data.get('publish')
instance.save() #book.save() django 的orm提供的
return instance
def create(self, validated_data):
instance=Book.objects.create(**validated_data)
return instance
# Book.objects.create(name=validated_data.get('name'))
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model=Book # 对应上models.py中的模型
fields='__all__'
# fields=('name','price','id','author') # 只序列化指定的字段
# exclude=('name',) #跟fields不能都写,写谁,就表示排除谁
# read_only_fields=('price',)
# write_only_fields=('id',) #弃用了,使用extra_kwargs
extra_kwargs = { # 类似于这种形式name=serializers.CharField(max_length=16,min_length=4)
'price': {'write_only': True},
}
app01/views.py
from django.shortcuts import render
# Create your views here.
from django.http import JsonResponse
from rest_framework.views import APIView
from app01.models import Book
from app01.ser import BookSerializer
from app01.ser import BookModelSerializer
from rest_framework.response import Response #drf 提供的响应对象
# 导入自己写的response类
from app01.utils import MyResponse
class BookView(APIView):
def get(self,request,pk):
book=Book.objects.filter(id=pk).first()
#用一个类,毫无疑问,一定要实例化
#要序列化谁,就把谁传过来
book_ser=BookSerializer(book) # 调用类的__init__
# book_ser.data 序列化对象.data就是序列化后的字典
return Response(book_ser.data)
# return JsonResponse(book_ser.data)
def put(self,request,pk):
response_msg={'status':100,'msg':'成功'}
# 找到这个对象
book = Book.objects.filter(id=pk).first()
# 得到一个序列化类的对象
# boo_ser=BookSerializer(book,request.data)
boo_ser=BookSerializer(instance=book,data=request.data)
# 要数据验证(回想form表单的验证)
if boo_ser.is_valid(): # 返回True表示验证通过
boo_ser.save() # 报错
response_msg['data']=boo_ser.data
else:
response_msg['status']=101
response_msg['msg']='数据校验失败'
response_msg['data']=boo_ser.errors
return Response(response_msg)
def delete(self,request,pk):
response=MyResponse()
ret=Book.objects.filter(pk=pk).delete()
return Response(response.get_dict)
class BooksView(APIView):
# def get(self,request):
# response_msg = {'status': 100, 'msg': '成功'}
# books=Book.objects.all()
# book_ser=BookSerializer(books,many=True) #序列化多条,如果序列化一条,不需要写
# response_msg['data']=book_ser.data
# return Response(response_msg)
def get(self,request):
response=MyResponse()
books=Book.objects.all()
book_ser=BookSerializer(books,many=True) #序列化多条,如果序列化一条,不需要写
response.data=book_ser.data
return Response(response.get_dict)
# 新增
def post(self,request):
response_msg = {'status': 100, 'msg': '成功'}
#修改才有instance,新增没有instance,只有data
book_ser = BookSerializer(data=request.data)
# book_ser = BookSerializer(request.data) # 这个按位置传request.data会给instance,就报错了
# 校验字段
if book_ser.is_valid():
book_ser.save()
response_msg['data']=book_ser.data
else:
response_msg['status']=102
response_msg['msg']='数据校验失败'
response_msg['data']=book_ser.errors
return Response(response_msg)
class BooksView2(APIView):
def get(self,request):
response=MyResponse()
books=Book.objects.all()
book=Book.objects.all().first()
book_ser=BookModelSerializer(books,many=True)
book_one_ser=BookModelSerializer(book)
print(type(book_ser))
print(type(book_one_ser))
response.data=book_ser.data
return Response(response.get_dict)
APIView源码分析
drf的Request类
drf序列化
序列化与反序列化介绍
需要在installed_app中注册一下'rest_framework',不然报错,如果使用Jsonresponse返回,则可以不用注册
序列化对象.data直接拿到json数据
可以将rest_framework的response替换成jsonresponse,前者只是给返回的数据做了一个界面美化。
序列化字段的类型
使用charfield就返回字符串格式的json
- 数据库定义的字段类型和序列化类写的字段可以不一样,都用charfield没问题
- 路由
- 序列化器
- 需要查看哪些字段就写哪些字段
- 字段选择
- 将从数据库取出的数据传给序列化器,再使用response转成json返回(使用drf带的response需要先把rf注册到app,否则报错模板不存在)
序列化字段选项
-
使用接口,put,post数据时做数据校验
-
put方法修改数据
-
之前的修改数据方式
-
接口规范控制:抽象类abc、抛异常
-
重写update,更新数据
-
集成的父类中有这个父类内部调用这个方法,优先使用重写的这个方法
序列化组件修改数据
自定义校验:局部钩子、全局钩子校验:指定字段校验(类似form)
数据保存之前,可以使用钩子校验规则
validate_字段名 #局部钩子
validate # 全局钩子
字段访问控制
- readonly只可以查看,不可以写的字段
- writeonly只可以写,不可以查
class BookSerializer(serializers.Serializer):
id=serializers.CharField(read_only=True)
name=serializers.CharField(max_length=16,min_length=4)
# price=serializers.DecimalField()
price=serializers.CharField(write_only=True,required=True)
author=serializers.CharField(validators=[check_author]) # validators=[] 列表中写函数内存地址
publish=serializers.CharField()
- get
- 序列化多条加many
from app01.models import Book
from app01.ser import BookSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
class BooksView(APIView):
def get(self,request):
response=MyResponse()
books=Book.objects.all()
book_ser=BookSerializer(books,many=True) #序列化多条,如果序列化一条,不需要写
response.data=book_ser.data
return Response(response.get_dict)
- 新增post
- 需要修改序列化类的create方法
ser.py
# from rest_framework.serializers import Serializer # 就是一个类
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
# 需要继承 Serializer
from app01.models import Book
class BookSerializer(serializers.Serializer):
id=serializers.CharField(read_only=True)
name=serializers.CharField(max_length=16,min_length=4)
# price=serializers.DecimalField()
price=serializers.CharField(write_only=True,required=True)
author=serializers.CharField(validators=[check_author]) # validators=[] 列表中写函数内存地址
publish=serializers.CharField()
def validate_price(self, data): # validate_字段名 接收一个参数
#如果价格小于10,就校验不通过
#如果价格小于10,就校验不通过
# print(type(data))
# print(data)
if float(data)>10:
return data
else:
#校验失败,抛异常
raise ValidationError('价格太低')
def validate(self, validate_data): # 全局钩子
print(validate_data)
author=validate_data.get('author')
publish=validate_data.get('publish')
if author == publish:
raise ValidationError('作者名字跟出版社一样')
else:
return validate_data
def update(self, instance, validated_data):
#instance是book这个对象
#validated_data是校验后的数据
instance.name=validated_data.get('name')
instance.price=validated_data.get('price')
instance.author=validated_data.get('author')
instance.publish=validated_data.get('publish')
instance.save() #book.save() django 的orm提供的
return instance
def create(self, validated_data):
instance=Book.objects.create(**validated_data)
return instance
# Book.objects.create(name=validated_data.get('name'))
views.py
from app01.models import Book
from app01.ser import BookSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
# 新增
def post(self,request):
response_msg = {'status': 100, 'msg': '成功'}
#修改才有instance,新增没有instance,只有data
book_ser = BookSerializer(data=request.data)
# book_ser = BookSerializer(request.data) # 这个按位置传request.data会给instance,就报错了
# 校验字段
if book_ser.is_valid():
book_ser.save()
response_msg['data']=book_ser.data
else:
response_msg['status']=102
response_msg['msg']='数据校验失败'
response_msg['data']=book_ser.errors
return Response(response_msg)
- 修改put
- 需要传book实例
- 需要重写update方法
from app01.models import Book
from app01.ser import BookSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
class BookView(APIView):
def put(self,request,pk):
response_msg={'status':100,'msg':'成功'}
# 找到这个对象
book = Book.objects.filter(id=pk).first()
# 得到一个序列化类的对象
# boo_ser=BookSerializer(book,request.data)
boo_ser=BookSerializer(instance=book,data=request.data)
# 要数据验证(回想form表单的验证)
if boo_ser.is_valid(): # 返回True表示验证通过
boo_ser.save() # 报错
response_msg['data']=boo_ser.data
else:
response_msg['status']=101
response_msg['msg']='数据校验失败'
response_msg['data']=boo_ser.errors
return Response(response_msg)
- delete删除
- 直接操作book对象,不需要重写方法
from app01.models import Book
from app01.ser import BookSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
def delete(self,request,pk):
response=MyResponse()
ret=Book.objects.filter(pk=pk).delete()
return Response(response.get_dict)
- 类封装返回信息
app1/uitls.py
class MyResponse():
def __init__(self):
self.status=100
self.msg='成功'
@property
def get_dict(self):
return self.__dict__
if __name__ == '__main__':
res=MyResponse()
res.status=101
res.msg='查询失败'
# res.data={'name':'lqz'}
print(res.get_dict)
- 类封装返回信息的使用
模型类的序列化器
- app/views.py
from rest_framework.views import APIView
from app01.models import Book
from app01.ser import BookModelSerializer
from rest_framework.response import Response #drf 提供的响应对象
# 导入自己写的response类
from app01.utils import MyResponse
class BooksView2(APIView):
def get(self,request):
response=MyResponse()
books=Book.objects.all()
book=Book.objects.all().first()
book_ser=BookModelSerializer(books,many=True)
book_one_ser=BookModelSerializer(book)
print(type(book_ser))
print(type(book_one_ser))
response.data=book_ser.data
return Response(response.get_dict)
- app/ser.py
- 对应上models.py中的模型,不需重写update和create
from rest_framework import serializers
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model=Book # 对应上models.py中的模型
fields='__all__'
# fields=('name','price','id','author') # 只序列化指定的字段
# exclude=('name',) #跟fields不能都写,写谁,就表示排除谁
# read_only_fields=('price',)
# write_only_fields=('id',) #弃用了,使用extra_kwargs
extra_kwargs = { # 类似于这种形式name=serializers.CharField(max_length=16,min_length=4)
'price': {'write_only': True},
}
- 不需要重写update和create了
![](https://upload-images.jianshu.io/upload_images/22042795-6a59fc1453cc0659.png?- imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - bookmodelser源码
- 序列化多条需要传many=true
- 加了many=true参数生成的是不同的对象
注册app
路由分发
- 先匹配总路由,再匹配多个app的路由
- include
Django--路由控制 - 刘清政 - 博客园
Serializer高级用法
source隐藏数据库字段,使用字段别名
- 尽量隐藏数据库字段,保证与序列化字段名不一样
小结
1 Serializer类,需要序列化什么,必须写一个类继承,想序列化什么字段,
就在里面写字段,source的作用(很多字段类)
2 序列化queryset(列表)对象和真正的对象,many=True的作用,instance=要序列化的对象,
3 反序列化 instance=要序列化的对象,data=request.data
4 字段验证,序列化类中,给字段加属性,局部和全局钩子函数,字段属性的validators=[check_author]
5 当在视图中调用 序列化对象.is_valid()
boo_ser.is_valid(raise_exception=True) 只要验证不通过,直接抛异常
6 修改保存---》调用序列化对象.save(),重写Serializer类的update方法
def update(self, instance, validated_data):
#instance是book这个对象
#validated_data是校验后的数据
instance.name=validated_data.get('name')
instance.price=validated_data.get('price')
instance.author=validated_data.get('author')
instance.publish=validated_data.get('publish')
instance.save() #book.save() django 的orm提供的
return instance
7 序列化得到字典,序列化对象.data
8 自己定义了一个Response对象
class MyResponse():
def __init__(self):
self.status=100
self.msg='成功'
@property
def get_dict(self):
return self.__dict__
9 反序列化的新增 序列化类(data=request.data),如果只传了data,当调用
序列化对象.save(),会触发序列化类的create方法执行,
当传了instance和data时,调用 序列化对象.save(),会触发序列化类的update方法执行
10 重写create方法(可以很复杂)
def create(self, validated_data):
instance=Book.objects.create(**validated_data)
return instance
11 ModelSerializer 跟Model做了一个对应
class BookModelSerializer(serializers.ModelSerializer):
def validate_price(self, data):
pass
publish=serializers.CharField(source='publish.name')
class Meta:
model=Book # 对应上models.py中的模型
fields='__all__'
# fields=('name','price','id','author','publish') # 只序列化指定的字段
# exclude=('name',) #跟fields不能都写,写谁,就表示排除谁
# read_only_fields=('price',)
# write_only_fields=('id',) #弃用了,使用extra_kwargs
extra_kwargs = { # 类似于这种形式name=serializers.CharField(max_length=16,min_length=4)
'price': {'write_only': True,max_length:16,min_length:4},
}
12 如果在ModelSerializer中写一个局部钩子或者全局钩子,如何写?
-跟之前一模一样
13 many=True 能够序列化多条的原因---》__new__是在__init__之前执行的,造出一个空对象
14 接口:统一子类的行为
drf-request和drf-response
- __getattr__方法
定义了__getattr__(),当访问object不存在的属性时会调用该方法
不定义访问不存在的属性时会报 AttributeError
eg:
class Cat(object):
def __init__(self):
self.name = "jn"
def __getattr__(self, item):
return "tm"
cat = Cat()
print(cat.name)
print(getattr(cat, 'name'))
print("*" * 20)
print(cat.age)
print(getattr(cat, 'age'))
getattr()函数
>>> getattr(a, 'bar') # 获取属性 bar 值
1
- assert断言该传入的request对象是django传入的request对象,如果不是则返回
请求对象.data:前端以三种编码方式传入的数据,都可以取出来
text/html
application/json
请求对象..query_params 与Django标准的request.GET相同,只是更换了更正确的名称而已。
drf-response
#from rest_framework.response import Response
def __init__(self, data=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
#data:你要返回的数据,字典
#status:返回的状态码,默认是200,
-from rest_framework import status在这个路径下,它把所有使用到的状态码都定义成了常量
#template_name 渲染的模板名字(自定制模板),不需要了解
#headers:响应头,可以往响应头放东西,就是一个字典
#content_type:响应的编码格式,application/json和text/html;
# 浏览器响应成浏览器json格式,postman响应成json格式,通过配置实现的(默认配置)
#不管是postman还是浏览器,都返回json格式数据
# drf有默认的配置文件---》先从项目的setting中找,找不到,采用默认的
# drf的配置信息,先从自己类中找--》项目的setting中找---》默认的找
-局部使用:对某个视图类有效
-在视图类中写如下
from rest_framework.renderers import JSONRenderer
renderer_classes=[JSONRenderer,]
-全局使用:全局的视图类,所有请求,都有效
-在setting.py中加入如下
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类
'rest_framework.renderers.JSONRenderer', # json渲染器
'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器浏览器访问时,会返回浏览器格式
)
}
- status响应状态码
视图
- 可以找到api.html页面修改当前显示
全局和局部配置响应
- 访问视图的查找顺序
视图
# 两个视图基类
APIView
GenericAPIView
基于GenericAPIView写5个接口
# views.py
class Book2View(GenericAPIView):
#queryset要传queryset对象,查询了所有的图书
# serializer_class使用哪个序列化类来序列化这堆数据
queryset=Book.objects
# queryset=Book.objects.all()
serializer_class = BookSerializer
def get(self,request):
book_list=self.get_queryset()
book_ser=self.get_serializer(book_list,many=True)
return Response(book_ser.data)
def post(self,request):
book_ser = self.get_serializer(data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response({'status':101,'msg':'校验失败'})
class Book2DetailView(GenericAPIView):
queryset = Book.objects
serializer_class = BookSerializer
def get(self, request,pk):
book = self.get_object()
book_ser = self.get_serializer(book)
return Response(book_ser.data)
def put(self, request,pk):
book = self.get_object()
book_ser = self.get_serializer(instance=book,data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response({'status': 101, 'msg': '校验失败'})
def delete(self,request,pk):
ret=self.get_object().delete()
return Response({'status': 100, 'msg': '删除成功'})
#url.py
# 使用GenericAPIView重写的
path('books2/', views.Book2View.as_view()),
re_path('books2/(?P<pk>d+)', views.Book2DetailView.as_view()),
- GenericAPIView源码
-GenricApiView的好处
基于GenericAPIView和5个视图扩展类写的接口
from rest_framework.mixins import ListModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin
# views.py
class Book3View(GenericAPIView,ListModelMixin,CreateModelMixin):
queryset=Book.objects
serializer_class = BookSerializer
def get(self,request):
return self.list(request)
def post(self,request):
return self.create(request)
class Book3DetailView(GenericAPIView,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin):
queryset = Book.objects
serializer_class = BookSerializer
def get(self, request,pk):
return self.retrieve(request,pk)
def put(self, request,pk):
return self.update(request,pk)
def delete(self,request,pk):
return self.destroy(request,pk)
# urls.py
# 使用GenericAPIView+5 个视图扩展类 重写的
path('books3/', views.Book3View.as_view()),
re_path('books3/(?P<pk>d+)', views.Book3DetailView.as_view()),
GenericApiView的9个视图子类
- 进一步省略代码
- 使用继承就可以使用
- get
- post
- 最终代码
retrieve获取一条,listApiView获取多条
1.2.3 GenericAPIView的视图子类
1)CreateAPIView
提供 post 方法
继承自: GenericAPIView、CreateModelMixin
2)ListAPIView
提供 get 方法
继承自:GenericAPIView、ListModelMixin
3)RetrieveAPIView
提供 get 方法
继承自: GenericAPIView、RetrieveModelMixin
4)DestoryAPIView
提供 delete 方法
继承自:GenericAPIView、DestoryModelMixin
5)UpdateAPIView
提供 put 和 patch 方法
继承自:GenericAPIView、UpdateModelMixin
6)RetrieveUpdateAPIView
提供 get、put、patch方法
继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
7)RetrieveUpdateDestoryAPIView
提供 get、put、patch、delete方法
继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
使用ModelViewSet编写5个接口
- 源码分析
- viewsets重写了路由的as_view方法
- 最终代码:路由增加一个参数,来区分get一条或者多条
代码
# views.py
from rest_framework.viewsets import ModelViewSet
class Book5View(ModelViewSet): #5个接口都有,但是路由有点问题
queryset = Book.objects
serializer_class = BookSerializer
# urls.py
# 使用ModelViewSet编写5个接口
path('books5/', views.Book5View.as_view(actions={'get':'list','post':'create'})), #当路径匹配,又是get请求,会执行Book5View的list方法
re_path('books5/(?P<pk>d+)', views.Book5View.as_view(actions={'get':'retrieve','put':'update','delete':'destroy'})),
源码分析ViewSetMixin
Python全栈开发29期_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili
# 重写了as_view
# 核心代码(所以路由中只要配置了对应关系,比如{'get':'list'}),当get请求来,就会执行list方法
for method, action in actions.items():
#method:get
# action:list
handler = getattr(self, action)
#执行完上一句,handler就变成了list的内存地址
setattr(self, method, handler)
#执行完上一句 对象.get=list
#for循环执行完毕 对象.get:对着list 对象.post:对着create
继承viewsetmixin视图类
- 自定义get方法使用某个函数
# views.py
from rest_framework.viewsets import ViewSetMixin
class Book6View(ViewSetMixin,APIView): #一定要放在APIVIew前,也可以继承genricapiview
def get_all_book(self,request):
print("xxxx")
book_list = Book.objects.all()
book_ser = BookSerializer(book_list, many=True)
return Response(book_ser.data)
# urls.py
#继承ViewSetMixin的视图类,路由可以改写成这样
path('books6/', views.Book6View.as_view(actions={'get': 'get_all_book'})),
https://tva1.sinaimg.cn/large/007S8ZIlgy1ggipvctuxcj31wc0goq89.jpg