今日内容概要
- 作业讲解
- Request类源码分析
- 序列化组件介绍
- 序列化类的基本使用
- 常用字段类和参数
- 反序列化之校验
- 反序列化之保存
作业讲解
写一个装饰器,装饰在fbv上,这个fbv可以接受前端的编码格式可以是urlencoded,form-data,json,取数据,都是从request.data中取
from django.http import JsonResponse
import json
def outer(func):
def inner(request,*args,**kwargs):
"""
urlencoded:request.POST
form-data:request.POST
json:request.body
"""
try:
request.data = json.loads(request.body) # # 如果是json格式,就成功;如果是其他格式,直接就报错了
except Exception :
request.data = request.POST
res = func(request, *args, **kwargs)
return res
return inner
@outer
def index(request):
if request.method == "POST":
print(request.data) # <QueryDict: {'name': ['西游记'], 'price': ['99']}>
print(request.FILES) # <MultiValueDict: {'file': [<InMemoryUploadedFile: 2.2.png (image/png)>]}>
return JsonResponse({'code':200,'msg':'ok','data':{'name':'nana','age':18}})
Request类源码分析
利用APIView+Response写接口
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
class BookViews(APIView):
def get(self,request):
# 需求获取所有书
book_list = Book.objects.all()
l1 = []
for book in book_list:
dic = {'name':book.name,'price':book.price}
l1.append(dic)
return Response({'code':200,'msg':'查询成功','data':l1})
def post(self,request):
# 需求创建一本书
print(request.data) # <QueryDict: {'name': ['三国演义'], 'price': ['99']}>
name = request.data.get('name')
price = request.data.get('price')
Book.objects.create(name=name,price=price)
return Response({'code':200,'msg':'创建成功','data':{'name': name, 'price':price}})
class BookDetails(APIView):
def get(self, request, pk):
# 需求 获取主键为1
book_obj = Book.objects.filter(pk=pk).first()
return Response({'code': 200, 'msg': '查询成功', 'data': {'name': book_obj.name, 'price': book_obj.price}})
def put(self,request,pk):
# 需求修改数据
print(request.data) # {'name': 'xiyouji', 'price': 666}
name = request.data.get('name')
price = request.data.get('price')
Book.objects.filter(pk=pk).update(name = name,price = price)
return Response({'code': 200, 'msg': '修改成功', 'data': {'name': name, 'price': price}})
def delete(self,request,pk):
# 需求:删除数据
Book.objects.filter(pk=pk).delete()
return Response({'code': 200, 'msg': '删除成功'})
Request类源码分析
1.根据APIView源码的分析:
当请求来了,会执行url中配置的:path('books/', BookView.as_view()),---->BookView.as_view()(request)---->APIView的as_view类方法---》内部调用的还是View的as_view--->self.dispatch---->APIView的dispatch
def dispatch(self, request, *args, **kwargs):
# request新的(rest_framework.request.Request), request老的(django.core.handlers.wsgi.WSGIRequest)
request = self.initialize_request(request, *args, **kwargs)
2.鼠标点:initialize_request,该函数返回Request方法
def initialize_request(self, request, *args, **kwargs):
parser_context = self.get_parser_context(request)
return Request(
request, # 旧的request
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
3.鼠标点:返回值Request()方法
class Request:
def __init__(self, request, ...)
self._request = request # 旧的request
pass
@property
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data
@property
def query_params(self):
return self._request.GET
def __getattr__(self, attr): # request._request.method
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
总结
1.新的request有个data属性,以后只要是在请求body体中的数据,无论什么编码格式,无论什么请求方式
ps:1.新的request类型:rest_framework.request.Request
2.旧的request类型:django.core.handlers.wsgi.WSGIRequest
3.request.data里面还可以有文件,但不要在里面取,还是用request.FILES
2.取文件还是从:request.FILES
3.取其他属性,跟之前完全一样 request.method ....
-原理是:新的Request重写了__getattr__,通过反射获取老的request中的属性
4.request.GET == >request.query_params
@property
def query_params(self):
return self._request.GET
"""
1.补充知识:魔法方法之 __getattr__
__getattr__:是点拦截(对象.属性),当属性不存在的时候,会自动触发类中的__getattr__
__getattribute__:对象获取属性的时候自动触发,无论这个属性存不存在。当类中既有getattr,又有getattribute的时候,只会执行getattribute
ps:https://juejin.cn/post/7126535355590770718
2.get请求能不能在body体中带数据--->能
"""
序列化组件介绍
1.功能--3个(序列化,反序列化、校验功能)
1. 序列化,序列化器会把模型对象(queryset,单个对象)转换成字典,经过response以后变成json字符串
2. 反序列化,把客户端发送过来的数据,经过request.data以后变成字典,序列化器可以把字典转成模型
3. 反序列化,完成数据校验功能
序列化类的基本使用
案例
创建book表模型-->APIView+Response+序列化 写接口
需求:查询单条和所有数据
Views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from .serializar import BookSerializer
class BookViews(APIView):
def get(self,request):
# 需求获取所有书
book_list = Book.objects.all()
"""
参数:instance--->实例,对象, data=empty--->前端传入的数据
many=True --->多条数据,如果是queryset对象,就要写
many=False--->单个对象,默认的
"""
# 使用序列化类,完成序列化
ser = BookSerializer(instance=book_list,many=True)
print(ser)
"""
BookSerializer(instance=<QuerySet [<Book: Book object (2)>, <Book: Book object (3)>]>, many=True):
id = IntegerField(required=False)
name = CharField()
price = IntegerField()
"""
print(ser.data) # [OrderedDict([('id', 2), ('name', '红楼梦'), ('price', 99)]), OrderedDict([('id', 3), ('name', '三国演义'), ('price', 99)])]
print(type((ser.data))) # <class 'rest_framework.utils.serializer_helpers.ReturnList'>
return Response({'code':200,'msg':'查询成功','data':ser.data})
class BookDetails(APIView):
def get(self, request, pk):
# 需求 获取主键为1
book_obj = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book_obj)
return Response({'code': 200, 'msg': '查询成功', 'data': ser.data})
serializer.py
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
# 要序列化的字段
id = serializers.IntegerField(required=False) # 前端传入数据,可以不填这个字段
name = serializers.CharField()
price = serializers.IntegerField()
urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookViews.as_view()),
path('books/<int:pk>/', views.BookDetails.as_view()),
]
总结
# 序列化类的使用
1.写一个类,继承serializers.Serializer
2.在类中写字段,要序列化的字段
eg: name = serializers.CharField()
3.在视图类中使用:(多条,单条)
serializer = BookSerializer(instance=book_list, many=True)
serializer = BookSerializer(instance=book)
常用字段类和参数
常见字段类
| 字段 | 字段构造方式 |
|---|---|
| BooleanField | BooleanField() |
| NullBooleanField | NullBooleanField() |
| CharField | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) |
| EmailField | EmailField(max_length=None, min_length=None, allow_blank=False) |
| RegexField | RegexField(regex, max_length=None, min_length=None, allow_blank=False) |
| SlugField | SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+ |
| URLField | URLField(max_length=200, min_length=None, allow_blank=False) |
| UUIDField | UUIDField(format=’hex_verbose’) format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" |
| IPAddressField | IPAddressField(protocol=’both’, unpack_ipv4=False, **options) |
| IntegerField | IntegerField(max_value=None, min_value=None) |
| FloatField | FloatField(max_value=None, min_value=None) |
| DecimalField | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置 |
| DateTimeField | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) |
| DateField | DateField(format=api_settings.DATE_FORMAT, input_formats=None) |
| TimeField | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) |
| DurationField | DurationField() |
| ChoiceField | ChoiceField(choices) choices与Django的用法相同 |
| MultipleChoiceField | MultipleChoiceField(choices) |
| FileField | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
| ImageField | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
| ListField | ListField(child=, min_length=None, max_length=None) |
| DictField | DictField(child=) |
1.常用的:IntegerField CharField DateTimeField DecimalField
2.ListField和DictField--->比较重要,后面了解
参数(用来数据校验)
选项参数:(CharField,IntegerField)
| 参数名称 | 作用 |
|---|---|
| max_length | 最大长度 |
| min_lenght | 最小长度 |
| allow_blank | 是否允许为空 |
| trim_whitespace | 是否截断空白字符 |
| max_value | 最小值 |
| min_value | 最大值 |
通用参数:
| 参数名称 | 说明 |
|---|---|
| read_only | 表明该字段仅用于序列化输出,默认False |
| write_only | 表明该字段仅用于反序列化输入,默认False |
| required | 表明该字段在反序列化时必须输入,默认True |
| default | 反序列化时使用的默认值 |
| allow_null | 表明该字段是否允许传入None,默认False |
| validators | 该字段使用的验证器 |
| error_messages | 包含错误编号与错误信息的字典 |
| label | 用于HTML展示API页面时,显示的字段名称 |
| help_text | 用于HTML展示API页面时,显示的字段帮助提示信息 |
ps:read_only 、write_only 很重要
反序列化
反序列化之校验
1.反序列化有三层校验
1.字段自己的(写的字段参数:required max_length 。。。)
2.局部钩子:写在序列化类中的方法,方法名必须是 validate_字段名
def validate_name(self, name):
if 'sb' in name:
# 不合法,抛异常
raise ValidationError('书名中不能包含sb')
else:
return name
3.全局钩子:写在序列化类中的方法 方法名必须是 validate
def validate(self, attrs):
price = attrs.get('price')
name = attrs.get('name')
if name == price:
raise ValidationError('价格不能等于书名')
else:
return attrs
字段自己的
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
# 要序列化的字段
id = serializers.IntegerField(required=False) # 前端传入数据,可以不填这个字段
name = serializers.CharField(max_length=8)
price = serializers.IntegerField(min_value=50,error_messages='价格不能低于50')
# view.py
def post(self,request):
# 需求创建一本书
# 1.前端会传入数据,request.data--->把这个数据保存到数据库中--->借助于序列化类,完成 校验和反序列化
ser = BookSerializer(data=request.data)
# 校验数据
if ser.is_valid():
print(ser.validated_data)
return Response({'code': 200, 'msg': '创建成功'}) # OrderedDict([('name', '三国演义'), ('price', 99)])
else:
print(ser.errors)
return Response({'code':200,'msg':'创建失败','errors':ser.errors})
"""
结果:
{
"code": 200,
"msg": "创建失败",
"errors": {
"price": [
"Ensure this value is greater than or equal to 50."
]
}
}
"""
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
# 要序列化的字段
id = serializers.IntegerField(required=False) # 前端传入数据,可以不填这个字段
name = serializers.CharField(max_length=8,allow_blank=True, required=False)
price = serializers.IntegerField(min_value=50,max_value=100,error_messages={'min_value':'价格不能低于50','max_value':'最高不超过100'})
局部钩子(给某个字段做验证)
# serializer.py
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
class BookSerializer(serializers.Serializer):
# 要序列化的字段
id = serializers.IntegerField(required=False) # 前端传入数据,可以不填这个字段
name = serializers.CharField(max_length=8)
price = serializers.IntegerField(min_value=50,max_value=100,error_messages={'min_value':'价格不能低于50','max_value':'最高不超过100'})
def validate_name(self,name):
# 需求:名字中不包含sb
if 'sb' in name:
raise ValidationError('不能含有sb')
else:
return name
def validate_price(self,price):
if price == 88:
raise ValidationError('价格能为88')
else:
return price
# views.py
def post(self,request):
# 需求创建一本书
# 1.前端会传入数据,request.data--->把这个数据保存到数据库中--->借助于序列化类,完成 校验和反序列化
ser = BookSerializer(data=request.data)
# 校验数据
if ser.is_valid():
print(ser.validated_data)
return Response({'code': 200, 'msg': '创建成功'}) # OrderedDict([('name', '三国演义'), ('price', 99)])
else:
print(ser.errors)
return Response({'code':200,'msg':'创建失败','errors':ser.errors})
"""
{
"code": 200,
"msg": "创建失败",
"errors": {
"name": [
"Ensure this field has no more than 8 characters."
],
"price": [
"价格能为88"
]
}
}
"""
全局钩子
def validate(self, attrs):
price = attrs.get('price')
name = attrs.get('name')
if name == price:
raise ValidationError('价格不能等于书名')
else:
return attrs
反序列化之保存
新增接口
1.序列化类的对象,实例化的时候:ser = BookSerializer(data=request.data)
2.数据校验过后---->调用 序列化类.save()--->但是要在序列化类中重写 create方法
def create(self, validated_data):
book=Book.objects.create(**validated_data)
return book
修改接口
1.序列化类的对象,实例化的时候:
ser = BookSerializer(instance=book,data=request.data)
2.数据校验过后----》调用 序列化类.save()--->但是要在序列化类中重写 update方法
def update(self, book, validated_data):
# instance 要修改的对象
# validated_data:前端传入,并且校验过后的数据
# book.name = validated_data.get('name')
# book.price = validated_data.get('price')
# 优化
for item in validated_data:
setattr(book,item,validated_data[item])
book.save()
return book
案例
urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookViews.as_view()),
path('books/<int:pk>/', views.BookDetails.as_view()),
]
Serializer.py
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import Book
class BookSerializer(serializers.Serializer):
# 要序列化的字段
id = serializers.IntegerField(required=False) # 前端传入数据,可以不填这个字段
name = serializers.CharField(max_length=8)
price = serializers.IntegerField(min_value=50,max_value=100,error_messages={'min_value':'价格不能低于50','max_value':'最高不超过100'})
def validate_name(self,name):
# 需求:名字中不包含sb
if 'sb' in name:
raise ValidationError('不能含有sb')
else:
return name
def validate_price(self,price):
if price == 88:
raise ValidationError('价格能为88')
else:
return price
def create(self, validated_data): # validated_data:校验过后的数据
book=Book.objects.create(**validated_data)
return book
def update(self, book, validated_data):
# instance 要修改的对象
# validated_data:前端传入,并且校验过后的数据
# book.name = validated_data.get('name')
# book.price = validated_data.get('price')
# 优化
for item in validated_data:
setattr(book,item,validated_data[item])
book.save()
return book
Views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from .serializar import BookSerializer
class BookViews(APIView):
def get(self,request):
# 需求获取所有书
book_list = Book.objects.all()
"""
参数:instance--->实例,对象, data=empty--->前端传入的数据
many=True --->多条数据,如果是queryset对象,就要写
many=False--->单个对象,默认的
"""
# 使用序列化类,完成序列化
ser = BookSerializer(instance=book_list,many=True)
print(ser)
"""
BookSerializer(instance=<QuerySet [<Book: Book object (2)>, <Book: Book object (3)>]>, many=True):
id = IntegerField(required=False)
name = CharField()
price = IntegerField()
"""
print(ser.data) # [OrderedDict([('id', 2), ('name', '红楼梦'), ('price', 99)]), OrderedDict([('id', 3), ('name', '三国演义'), ('price', 99)])]
print(type((ser.data))) # <class 'rest_framework.utils.serializer_helpers.ReturnList'>
return Response({'code':200,'msg':'查询成功','data':ser.data})
def post(self,request):
# 需求创建一本书
# 1.前端会传入数据,request.data--->把这个数据保存到数据库中--->借助于序列化类,完成 校验和反序列化
ser = BookSerializer(data=request.data)
# 校验数据
if ser.is_valid(): # 三层校验通过
print(ser.validated_data) # validated_data:校验过后的数据
# 保存数据
ser.save()
return Response({'code': 200, 'msg': '创建成功'}) # OrderedDict([('name', '三国演义'), ('price', 99)])
else:
print(ser.errors)
return Response({'code':200,'msg':'创建失败','errors':ser.errors})
class BookDetails(APIView):
def get(self, request, pk):
# 需求 获取主键为1
book_obj = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book_obj)
return Response({'code': 200, 'msg': '查询成功', 'data': ser.data})
def put(self,request,pk):
book_obj = Book.objects.filter(pk=pk).first()
# 需求修改数据
ser = BookSerializer(instance=book_obj,data=request.data)
if ser.is_valid():
print(ser.validated_data) # OrderedDict([('name', 'xiyouji'), ('price', 89)])
ser.save()
return Response({'code': 200, 'msg': '修改成功'})
else:
print(ser.errors)
return Response({'code': 200, 'msg': '修改失败', 'errors':ser.errors})
def delete(self,request,pk):
# 需求:删除数据
Book.objects.filter(pk=pk).delete()
return Response({'code': 200, 'msg': '删除成功'})
作业
1.Publish的5个接口
-name
-addr
-phone
1.出版社名字不能包含东京,最大长度不能超过10,最短不能短于2
2.地址长度不能超过20,没有最短现在,地址不能以上海开头
3.手机号必须11位
4.出版社名字和地址名字不能一样
"""
1.自己写的
name--->max_length = 10,min_length=2
addr--->max_length = 20,
2.局部钩子
name--->validate_name--->in
addr--->validate_addr--->startswith
phone--->validate_phone--->len()
3.全局钩子
validate---->name != addr
"""
urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('publish/',views.PublishView.as_view() ),
path('publish/<int:pk>/',views.PublishDetail.as_view() ),
]
Serializer.py
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import Publish
class PublishSerializer(serializers.Serializer):
# id = serializers.IntegerField(required=False)
name = serializers.CharField(max_length=10,min_length=2,error_messages={'max_length':'名字最大不能10位','min_length':'名字最小不能2位'})
addr = serializers.CharField(max_length=20,error_messages={'max_length':'地址最大20位'})
phone = serializers.CharField()
def validate_name(self,name):
if '东京' in name:
raise ValidationError('出版社名字不能包含东京')
else:
return name
def validate_addr(self,addr:str):
if addr.startswith('上海'):
raise ValidationError('出版社地址不能以上海开头')
else:
return addr
def validate_phone(self,phone):
if len(phone) !=11:
raise ValidationError('出版社电话必须11位')
else:
return phone
def validate(self, attrs):
# print(attrs)
name = attrs.get('name')
addr = attrs.get('addr')
if name == addr:
raise ValidationError('出版社的地址和名字不能一样')
else:
return attrs
def create(self, validated_data):
publish = Publish.objects.create(**validated_data)
return publish
def update(self, publish, validated_data):
for item in validated_data:
setattr(publish,item,validated_data[item])
publish.save()
return publish
views.py
from django.shortcuts import render
# Create your views here.
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Publish
from .serializer import PublishSerializer
class PublishView(APIView):
def get(self,request):
publish_list = Publish.objects.all()
ser = PublishSerializer(instance=publish_list,many=True)
return Response({'code':200,'msg':'查询成功','data':ser.data})
def post(self,request):
# 新增出版社
ser = PublishSerializer(data=request.data)
# 校验数据
if ser.is_valid():
print(ser.validated_data)
ser.save()
return Response({'code': 200, 'msg': '添加成功', 'data': ser.data})
else:
print(ser.errors)
return Response({'code': 200, 'msg': '添加失败', 'errors_msg':ser.errors})
class PublishDetail(APIView):
def get(self,request,pk):
publish_obj = Publish.objects.filter(pk=pk).first()
ser = PublishSerializer(instance=publish_obj)
return Response({'code': 200, 'msg': '查询成功', 'data': ser.data})
def put(self,request,pk):
publish_obj = Publish.objects.filter(pk=pk).first()
# 修改数据
ser = PublishSerializer(instance=publish_obj,data=request.data)
# 校验数据
if ser.is_valid():
print(ser.validated_data)
ser.save()
return Response({'code': 200, 'msg': '修改成功', 'data': ser.data})
else:
print(ser.errors)
return Response({'code': 200, 'msg': '修改失败', 'errors_msg': ser.errors})
def delete(self,request,pk):
Publish.objects.filter(pk =pk).delete()
return Response({'code': 200, 'msg': '删除成功'})