内容回顾
# 1 restful规范--10条
# 2 restful快速使用---》快速演示--》5个接口--》所有接口,都是5个的变形
-查询所有---》get /books/
-查询单个---》get /books/5/
-修改一个---》put,patch /books/5/ --->需要在body体中带数据---》编码格式
-新增一个---》post----》/books/----》body体中带数据
-删除一个---》delete--->/books/5/---》前端看到这条数据---》删除
# 3 cbv源码分析
-路由---》视图类.as_view()---》as_view执行结果是一个函数内存地址---》view闭包函数---》当请求来了,路由匹配成功,执行view(request)--->view闭包函数:self.dispatch---》dispatch--->通过请求方式 使用反射去视图类的对象中反射出跟请求方式同名的方法执行
-as_view类的绑定方法----》装饰器---》classmethod---》绑定给类的,类来调用,自动把类传入---》对象调用?
# 4 什么是闭包函数?---》跟语言无关
-1 定义在函数内部---》内层函数
-2 对外部作用域有引用--》引用,如果外层变了,闭包函数引用的值,也变了
-闭包是多了一种函数传参的方式---》写在括号中,使用实参传入函数中----》包到函数中,使用外包作用域的变量(引用)
# 5 APIView的执行流程---》继承了View---》重写了as_view(csrf禁用了)和dispatch(在执行视图函类的方法之前干了好多事)
-比之前cbv执行流程多了
-1 把老的request对象封装成了新的drf的Request对象
-在视图函数中使用的request都是新的
-老的request在新的request._request
-视图函数对象.request 就是新的request
-新的request使用起来跟老的一模一样,当老的使用即可
-新的request多了个data (方法包装成数据属性)--》body体中的数据都在data中
-request.data--->是哪个类的对象?
-urlencoded,formdata:QueryDict
-json:字典对象
-看看QueryDict的源码---》type这个对象---》找到它属于哪个类---》导入类,点进去看
print(type(request.POST)) # 老的POST
from django.http.request import QueryDict
MultiValueDict--》MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})----》form表单提交数据多个重名数据---》request.POST.get('user')--->只能获取到1个,向全取出来使用getlist
-2 在执行视图函数之前---》三大认证
-认证
-权限
-频率
-3 处理了全局异常
-视图类中,三大认证过程中---》出了异常,我们都能处理----》处理全局异常
# 6 新的Request类源码
-request.data
-重写了 __getattr__----》. 拦截魔法方法---》对象.属性如果属性不存在,就会触发__getattr__执行
-新的request.method---》通过反射,去request._request中反射出method属性
-算但不是---》本意是隐藏起来不让外部用---》只用了一个_ __才叫隐藏属性---》约定俗称,你们公司里 属性,方法 _属性/方法
内容概要
序列化器介绍
Serializer的使用
反序列化之新增
反序列化之修改
额外添加参数
1 序列化器介绍
# 序列化器:类,作用
1. 序列化,序列化器会把模型对象(qs,book)转换成字典,经过response以后变成json字符串
2. 反序列化,把客户端发(前端)送过来的数据,经过request以后变成字典(data),序列化器可以把字典转成模型-->存到数据库中
3. 反序列化,完成数据校验功能---》前端传入的数据是否合法,长度够不够。。。数据校验
# 使用方式
-第一步:写一个类:必须继承drf中的Serializer及其子类
-第二步:在类中写要序列化的字段-->要序列化哪些,就写哪些,不序列化的不写
-第三步:使用序列化类,视图类中用
-得到序列化类对象 对象.data,通过Response返回给前端
2 Serializer的使用
2.1 基本使用--序列化
**序列化类**
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.Serializer):
# max_length=32,min_length=3 反序列化保存校验数据的时候用
name = serializers.CharField(max_length=8, min_length=3)
price=serializers.CharField() # models中使用了DecimalField,这个位置使用了CharField会把小数类型转成字符串
author = serializers.CharField()
**视图类**
class BookView(APIView):
def get(self,request):
# 查出来的数据做序列化
book_list=Book.objects.all()
# instance:要序列化的对象 qs,单个对象
# many:如果是qs对象,many=True,如果是单个对象many=False
ser=BookSerializer(instance=book_list,many=True) # 传入初始化的参数instance=None, data=empty
# ser.data使用模型类对象序列化后的字典
return Response(ser.data) # 字典,列表,字符串都行
**路由**
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookView.as_view()),
]
**模型类**
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(decimal_places=2, max_digits=5)
author = models.CharField(max_length=32)
2.2 字段类型
# models中CharField,DecimalField.....
# 记住的:
CharField
IntegerField
FloatField
DecimalField
DateTimeField
DateField
# 知道--》models中没有的---》反序列化的时候,前端传入的
{name:lqz,age:19,hobby:[篮球,足球],wife:{name:lyf,age:38}}
ListField
DictField
字段 |
字段构造方式 |
---|---|
**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(max*length=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=) |
2.3 字段参数
# 写在字段类中的参数--》如:max_length。。。
# 重点:
read_only:表明该字段仅用于序列化输出,默认False
-如果read_only=True,这个字段只用来做序列化---》把对象---》json给前端
write_only:表明该字段仅用于反序列化输入,默认False
-如果read_only=write_only,这个字段只用来做反序列化---》前端json---》存到数据库
# 举例
name=serializers.CharField(max_length=32,min_length=3)
price=serializers.CharField(write_only=True,read_only=True)
author=serializers.CharField(write_only=True)
# 什么都不写,表示既序列化,又反序列化
# 序列化给前端,前端看到的字段样子---》只能看到name
# 反序列化,前端需要传什么过来name,price,author都传
# write_only=True,read_only=True 逻辑矛盾
**选项参数:**
参数名称 |
作用 |
---|---|
**max_length** |
最大长度(CharField) |
**min_lenght** |
最小长度(CharField) |
**allow_blank** |
是否允许为空(CharField) |
**trim_whitespace** |
是否截断空白字符(CharField) |
**max_value** |
最小值 (IntegerField) |
**min_value** |
最大值(IntegerField) |
**通用参数**:
参数名称 |
说明 |
---|---|
**read_only** |
表明该字段仅用于序列化输出,默认False |
**write_only** |
表明该字段仅用于反序列化输入,默认False |
**required** |
表明该字段在反序列化时必须输入,默认True |
**default** |
反序列化时使用的默认值 |
**allow_null** |
表明该字段是否允许传入None,默认False |
**validators** |
该字段使用的验证器(不太用) |
**error_messages** |
包含错误编号与错误信息的字典 |
**label** |
用于HTML展示API页面时,显示的字段名称 |
**help_text** |
用于HTML展示API页面时,显示的字段帮助提示信息 |
2.4 序列化时,定制序列化的字段
### 两种方案
# 作用?比如author关联了外键,想取出作者详情
# author_info = serializers.SerializerMethodField()
# def get_author_info(self,obj):
# return {'name':obj.author.name,'age':obj.author.age}
# 第一种:在序列化类中写
# price_info = serializers.SerializerMethodField() # 必须配合一个方法,方法名get_字段名
# def get_price_info(self, obj): # 方法返回什么,字段就是什么,obj是当前序列化到的单个对象
# return "价格是:"+str(obj.price) # python是动态强类型语言--》强类型是:不同类型之间不允许直接运算
# 第二种方案:在models中写方法,这里字段名就是models的方法名,price_info方法返回什么,这个字段就是什么
# price_info=serializers.CharField()
authors=serializers.ListField()
**序列化类中写**
price_info = serializers.SerializerMethodField() # 必须配合一个方法,方法名get_字段名
def get_price_info(self, obj): # 方法返回什么,字段就是什么,obj是当前序列化到的单个对象
return "价格是:"+str(obj.price) # python是动态强类型语言--》强类型是:不同类型之间不允许直接运算
**表模型中写**
# 表模型中写方法
@property # 把方法包装成数据属性
def price_info(self):
return "价格是:" + str(self.price)
# 序列化类中使用
price_info=serializers.CharField(read_only=True) # 只用来做序列化
# authors=serializers.ListField()
3 反序列化之新增
# 反序列化
第一步:把前端传入的数据,放到Serializer对象中:ser=BookSerializer(data=request.data)
第二步:校验数据:ser.is_valid():
第三步:保存,ser.save()---》必须重写create,在序列化类中
def create(self, validated_data):
book=Book.objects.create(**validated_data)
return book
视图类
class BookView(APIView):
def post(self,request):
# 反序列化,保存---》使用data参数
ser=BookSerializer(data=request.data) #
# 校验数据
if ser.is_valid(): # 如果是true表示数据校验通过,通过,就保存
# 如果instance为None,调用save本质会调用create--》父类create直接抛异常,所以我们要重写
ser.save() # 就会保存,重写create方法,如果不重写,我们不知道存到哪个表中
return Response(ser.data)
return Response({'code':101,'msg':'数据校验失败','err':ser.errors})
序列化类
from rest_framework import serializers
from .models import Book
# drf提供的类:Serializer
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=8, min_length=3)
price=serializers.CharField()
author = serializers.CharField()
price_info=serializers.CharField(read_only=True) # 只用来做序列化
def create(self, validated_data):
# validated_data校验过后的数据
# 手动存到book表中
book=Book.objects.create(**validated_data)
# book=Book.objects.create(name=validated_data.get('name'))
return book # 不要忘记返回book对象
补充国际化
# 配置文件中改
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
4 反序列化之修改
**序列化类**
class BookSerializer(serializers.Serializer):
id = serializers.CharField(read_only=True)
# max_length=32,min_length=3 反序列化保存校验数据的时候用
name = serializers.CharField(max_length=8, min_length=3)
price = serializers.CharField(required=False) # models中使用了DecimalField,这个位置使用了CharField会把小数类型转成字符串
author = serializers.CharField(required=False)
def create(self, validated_data):
# validated_data校验过后的数据
# 手动存到book表中
book = Book.objects.create(**validated_data)
# book=Book.objects.create(name=validated_data.get('name'))
return book # 不要忘记返回book对象
def update(self, instance, validated_data):
# validated_data校验过后的数据,instance 是要修改的对象
instance.name = validated_data.get('name')
instance.price = validated_data.get('price')
instance.author = validated_data.get('author')
instance.save() # 模型对象自带的save,保存到数据库中
return instance # 要忘记返回instance对象
# 局部钩子
# 字段有自己的校验:max_length .... ,再校验,就可以写局部钩子
def validate_name(self, attr):
# attr就是前端传入的数据
# 名字不能以sb开头
if attr.startswith('sb'):
raise ValidationError("名字不能以sb开头")
else:
return attr # 没有问题,正常返回
**视图类**
class BookDetailView(APIView):
def get(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book) # 如果是单条记录,many不传,就是false
return Response(ser.data)
def delete(self, request, pk):
res = Book.objects.filter(pk=pk).delete()
print(res)
if res[0] > 0:
return Response({'code': 100, 'msg': '删除成功'})
else:
return Response({'code': 103, 'msg': '数据不存在'})
def put(self, request, pk):
# 修改:用什么数据,修改哪个对象?
book = Book.objects.filter(pk=pk).first()
# 既有instance,又有data,表示修改
ser = BookSerializer(instance=book, data=request.data)
if ser.is_valid():
# 重写update方法
ser.save() # 调用save---》内部根据instance判断是触发create还是update
return Response(ser.data)
return Response({'code': 102, 'msg': '修改出错', 'err': ser.errors})
**路由**
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookView.as_view()),
path('books/<int:pk>', views.BookDetailView.as_view()),
]
4.1 反序列化之局部和全局钩子
# 局部钩子
# 字段有自己的校验:max_length .... ,再校验,就可以写局部钩子
def validate_name(self, attr):
# attr就是前端传入的数据
# 名字不能以sb开头
if attr.startswith('sb'):
raise ValidationError("名字不能以sb开头")
else:
return attr # 没有问题,正常返回
# 先走字段自己规则,再走局部钩子,再走全局钩子
# 全局钩子
def validate(self, attrs):
# attrs校验过后的数据
if attrs.get('name') == attrs.get('author'):
raise ValidationError('作者名不能等于书名')
else:
return attrs
4 ModelSerializer模型类序列化器
# 以后使用ModelSerializer跟表模型做绑定,以后这个用的多,不需要重写update和create方法了
class BookSerializer2(serializers.ModelSerializer):
class Meta:
model = Book # 跟那个表有关系
# fields='__all__' # 所有字段
# id是从表模型中映射过来的,auto,它会不要求你传
# price_info:它不是数据库中字段
# 即便扩写的字段,也要在fields中注册
fields = ['id', 'name', 'price', 'author', 'price_info']
# 原来的字段参数,通过extra_kwargs传进去
extra_kwargs = {
'name': {'write_only': True, 'max_length': 8, 'min_length': 3}
}
# 重写字段
# 局部和全局钩子跟之前一样
5 额外添加参数
#
class BookSerializer2(serializers.ModelSerializer):
class Meta:
# 原来的字段参数,通过extra_kwargs传进去
extra_kwargs = {
'name': {'write_only': True, 'max_length': 8, 'min_length': 3}
}
1 author单表的5个接口与使用 Serializer和ModelSerializer实现
2 book 关联表---》author,publish关联,实现book的5个接口