一、drf-初步使用
1.下载
pip install djangorestframework
2注册:
核心配置:
INSTALLED_APPS = [
# 'django.contrib.admin',
# 'django.contrib.auth',
# 'django.contrib.contenttypes',
# 'django.contrib.sessions',
# 'django.contrib.messages',
'django.contrib.staticfiles',
'api.apps.ApiConfig',
'rest_framework'
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
# 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
# 'django.contrib.auth.middleware.AuthenticationMiddleware',
# 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
# 'django.contrib.auth.context_processors.auth',
# 'django.contrib.messages.context_processors.messages',
],
},
},
]
REST_FRAMEWORK = {
"UNAUTHENTICATED_USER": None #配置没登陆时匿名用户为空
}
配置匿名用户为空后,使用request.user就不会报错ContentType3.使用:使用rest_framework 的Response返回会显示一个页面
使用drf时,视图函数可以用函数也可以用类编写:
二、drf-FBV和CBV
FBV基于函数编写,需要自己编写if判断请求执行方式
CBV基于类编写,会自动根据请求方式执行对应的方法
drf的view继承了django的view,名称为APIView
三、drf的request对象和参数
面向对象的__getattr__:当调用对象不存在成员时,会执行__getattr__,否则就会报错
面向对象的__getattribute__:只要执行对象.成员,无论对象有没有这个成员,都会执行__getattribute__
drf的request对象利用了以上思想,对django的request方法进行封装,当执行drf的request中不存在的成员时,会去django的request方法执行,都没有则报错。
参数
可以通过变量名来接收路由参数,也可以通过self.args和self.kwarg接收。args为元组类型,kwargs为字典类型。
四、drf的认证组件
在开发API过程中,有些功能需要登录才能访问,有些无需登录。drf中的认证组件主要就是用来实现此功能。
4.1 认证组件使用
1.编写认证类
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
#做用户认证,读取请求传递的token,校验合法性
#返回值:返回元组,抛出异常,返回None
token= request.query_params.get("token") #query_params其实就是对django的request的GET封装,去url中获取token
if token:
return "lmj",token #返回元组,分别被`request.user` 和 `request.auth`获取
raise AuthenticationFailed({"code":444,"error":"认证失败"})
2.在视图中应用
如果应用组件的视图很多,可以进行全局配置:默认先在全局找认证,再去视图里找,视图如果设置了就以视图里为主。
#drf配置
REST_FRAMEWORK={
"UNAUTHENTICATED_USER":None,
"DEFAULT_AUTHENTICATION_CLASSES":["ext.auth.MyAuthentication",] #指定位置,注意不放在view里
}
4.2 多个认证组件
多个认证类都返回None,都没有认证成功 -> 视图函数会执行,只不过 self.user self.auth = None。
如果需要从不同的位置取token值,最后没取到再报异常可以这样写:
class URLAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.query_params.get("token")
if token:
return "wupeiqi", token
return
class HeaderAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.meta.get("xxxxx") #去请求头中找
if token:
return "wupeiqi", token
return
class BodyAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.data.get("xxxxx") #去请求体中找
if token:
return "wupeiqi", token
return
class NoAuthentication(BaseAuthentication):
def authenticate(self, request):
raise 异常认证失败
class OrderView(APIView):
authentication_classes = [URLAuthentication,HeaderAuthentication,BodyAuthentication ,NoAuthentication]
def get(self, request):
print(request.user, request.auth)
return Response("OrderView")
4.3 状态码一致
在认证类中加上authenticate_header方法
4.4 子类约束
drf的认证类必须写def authenticate这个方法,否则就会报错。
raise NotImplementedError
五、 drf权限组件
认证组件 = [认证类,认证类,认证类,]
1.认证成功或失败,不会在执行后续的认证类
2.返回None,执行后续的认证类。
权限组件 = [权限类,权限类,权限类…]
1.执行所有的权限类,True表示通过
2.默认情况,需满足所有权限类都返回True
5.1 权限组件的使用
与认证组件类似,需编写权限组件类:
class PermissionA(BasePermission):
message = {"code": 1003, 'data': "无权访问"} #无权时返回的信息
def has_permission(self, request, view):
if request.user.role == 2:
return True
return False
# 暂时先这么写
def has_object_permission(self, request, view, obj):
return True
5.2 多个权限组件
应用多个权限时,只有都返回True,才算通过
5.3 修改多个权限机制
如果需要修改不是都满足为True才算通过,就需要修改chek_permission的机制。
from rest_framework.views import APIView
class NbApiView(APIView):
def check_permissions(self, request):
no_permission_objects = []
for permission in self.get_permissions():
if permission.has_permission(request, self):
return #只要一个成功就返回,后续的不执行
else:
no_permission_objects.append(permission) #认证失败的权限
else:
self.permission_denied(
request,
message=getattr(no_permission_objects[0], 'message', None), #取无权限的第一个
code=getattr(no_permission_objects[0], 'code', None)
)
REST_FRAMEWORK = {
"UNAUTHENTICATED_USER": None,
"DEFAULT_AUTHENTICATION_CLASSES": [
"ext.auth.QueryParamsAuthentication",
"ext.auth.HeaderAuthentication",
"ext.auth.NoAuthentication",
],
"DEFAULT_PERMISSION_CLASSES":[
"ext.per.MyPermission"
]
}
六、 drf限流
限流,限制用户访问频率,例如:用户1分钟最多访问100次 或者 短信验证码每天可以发送50次, 防止盗刷。
5.1 限流组件的使用
编写限流组件:
from rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache as default_cache
class MyThrottle(SimpleRateThrottle):
scope = "XXX" # 构造缓存中的key
THROTTLE_RATES = {"XXX": "5/m"} #一分钟访问5次
cache = default_cache #指定redis存放
def get_cache_key(self, request, view):
if request.user:
ident = request.user.pk # 用户ID
else:
ident = self.get_ident(request) # 获取请求用户IP(去request中找请求头)
return self.cache_format % {'scope': self.scope, 'ident': ident}
配置redis:放置访问记录
pip install django-redis
# settings.py
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "qwe123",
}
}
}
5.2 登录限流与匿名限流
配置限流时间:
REST_FRAMEWORK = {
"UNAUTHENTICATED_USER": None,
"DEFAULT_AUTHENTICATION_CLASSES": [
"ext.auth.QueryParamsAuthentication",
"ext.auth.HeaderAuthentication",
"ext.auth.NoAuthentication",
],
"DEFAULT_THROTTLE_RATES": {
"ip": "10/m",
"user": "5/m"
}
# "DEFAULT_PERMISSION_CLASSES":[
# "ext.per.MyPermission"
# ]
}
编写登录限流和匿名限流:
应用多个限流时,有一个触发就异常
七、 GET传递版本
7.1 通过参数传递
导入类后可以在request.version中获取版本信息。
如果要改变version默认传递名称,则配置:
# settings.py
REST_FRAMEWORK = {
"VERSION_PARAM": "v",
"DEFAULT_VERSION": "v1", #不传递参数时的默认version
"ALLOWED_VERSIONS": ["v1", "v2", "v3"],
"DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.QueryParameterVersioning"
}
反向生成时,携带version版本:
7.2 通过url传递(使用较多)
7.2 通过请求头传递
八、 drf解析器
用于解析post请求体中的数据,全局设置
REST_FRAMEWORK = {
"UNAUTHENTICATED_USER": None,
"VERSION_PARAM": "version",
"DEFAULT_VERSION": "v1",
"ALLOWED_VERSIONS": ["v1", "v2"],
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
"DEFAULT_PARSER_CLASSES": ["rest_framework.parsers.JSONParser", ]
}
8.1 JSONParser解析器
请求头为aplication/json.传递的为json数据时解析
8.2 FormParser解析器
8.3 FileUploadParser解析器
8.4 MultiPartParser解析器
九、 drf序列化器
将从数据库获取的query_set或者数据对象转换为json
9.1 Serializer
from django.db import models
class Role(models.Model):
title = models.CharField(verbose_name="标题", max_length=32)
order = models.IntegerField(verbose_name="顺序")
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import models
class InfoSerializer(serializers.Serializer):
id = serializers.IntegerField() #与数据库字段相同
title = serializers.CharField()
order = serializers.IntegerField()
class InfoView(APIView):
def get(self, request):
# 1.数据库获取多条数据
# queryset = models.Role.objects.all()
# ser = InfoSerializer(instance=queryset, many=True) #多个数据时many=True进行都序列化
# 2.数据库获取单条数据
instance = models.Role.objects.all().first()
ser = InfoSerializer(instance=instance, many=False) #单个对象时many=False
print(type(ser.data), ser.data)
return Response(ser.data)
9.2 ModelSerializer
当serializers中字段较多,如果都重新写就很麻烦。
from django.db import models
class Role(models.Model):
title = models.CharField(verbose_name="标题", max_length=32)
order = models.IntegerField(verbose_name="顺序")
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import models
class InfoSerializer(serializers.ModelSerializer):
class Meta:
model = models.Role
# fields = "__all__" #将所有的字段都序列化
# fields = ['id', 'title', 'order'] #将指定的字段序列化
exclude = ["id"] #排除
class InfoView(APIView):
def get(self, request):
# 1.数据库获取多条数据
# queryset = models.Role.objects.all()
# ser = InfoSerializer(instance=queryset, many=True)
# 2.数据库获取单条数据
instance = models.Role.objects.all().first()
ser = InfoSerializer(instance=instance, many=False)
print(type(ser.data), ser.data)
return Response(ser.data)
9.3 source、format和自定义方法返回序列化值
source用于获取外键或者choices字段的中文信息,对时间类型进行定制,新增字段等
format=“%Y-%m-%d” #指定显示时间类型
from django.db import models
class Role(models.Model):
title = models.CharField(verbose_name="标题", max_length=32)
order = models.IntegerField(verbose_name="顺序")
class UserInfo(models.Model):
name = models.CharField(verbose_name="姓名", max_length=32)
gender = models.SmallIntegerField(verbose_name="性别", choices=((1, "男"), (2, "女")))
role = models.ForeignKey(verbose_name="角色", to="Role", on_delete=models.CASCADE)
ctime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import models
class InfoSerializer(serializers.ModelSerializer):
gender = serializers.CharField(source="get_gender_display") #source实际执行为obj.get_gender_display
role = serializers.CharField(source="role.title") #role外键存储的是id,若想跨表获取title,需执行obj.role.title,所以source的值就为role.title
ctime = serializers.DateTimeField(format="%Y-%m-%d") #指定显示时间类型
other_name = serializers.CharField(source="name")
mine = serializers.SerializerMethodField() #自定义序列化字段,会返回get_mine方法的返回值
class Meta:
model = models.UserInfo
fields = ['id', 'name', 'gender', "role", 'ctime', "other_name", "mine"]
def get_mine(self, obj):
return "x-x-{}".format(obj.name) #自定义序列化方法
class InfoView(APIView):
def get(self, request):
queryset = models.UserInfo.objects.all()
ser = InfoSerializer(instance=queryset, many=True)
print(type(ser.data), ser.data)
return Response(ser.data)
9.4 序列化类嵌套
ForeignKey字段跨表获取的是一个值
ManyToManyField字段跨表获取的是多个对象,需要对每一个对象再进行序列化返回:
1.可以使用自定义方法实现
2.可以使用序列化嵌套(*)
from django.db import models
class Role(models.Model):
title = models.CharField(verbose_name="标题", max_length=32)
order = models.IntegerField(verbose_name="顺序")
class Tag(models.Model):
caption = models.CharField(verbose_name="名称", max_length=32)
class UserInfo(models.Model):
name = models.CharField(verbose_name="姓名", max_length=32)
gender = models.SmallIntegerField(verbose_name="性别", choices=((1, "男"), (2, "女")))
role = models.ForeignKey(verbose_name="角色", to="Role", on_delete=models.CASCADE)
ctime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)
tags = models.ManyToManyField(verbose_name="标签", to="Tag")
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import models
class InfoSerializer(serializers.ModelSerializer):
gender = serializers.CharField(source="get_gender_display") #source实际执行为obj.get_gender_display
role = serializers.CharField(source="role.title") #role外键存储的是id,若想跨表获取title,需执行obj.role.title,所以source的值就为role.title
ctime = serializers.DateTimeField(format="%Y-%m-%d") #指定显示时间类型
other_name = serializers.CharField(source="name")
mine = serializers.SerializerMethodField() #自定义序列化字段,会返回get_mine方法的返回值
class Meta:
model = models.UserInfo
fields = ['id', 'name', 'gender', "role", 'ctime', "other_name", "mine"]
def get_mine(self, obj):
query_set=obj.tags.all() #获取对应tags的所有对象
result=[{"id":tag.id,"caption":tag.caption} for tag in query_set]
return result
class InfoView(APIView):
def get(self, request):
queryset = models.UserInfo.objects.all()
ser = InfoSerializer(instance=queryset, many=True)
print(type(ser.data), ser.data)
return Response(ser.data)
使用序列化嵌套实现:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import models
class D1(serializers.ModelSerializer):
class Meta:
model =models.Role
fields="__all__"
class D2(serializers.ModelSerializer):
class Meta:
model =models.Tag
fields="__all__"
class InfoSerializer(serializers.ModelSerializer):
role=D1()
tags=D2(many=True) #manytomany需要指定True
class Meta:
model = models.UserInfo
fields = [ 'name', 'age',"role", 'tags']
9.5 序列化继承
继承后直接使用父序列化器中的字段
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import models
class MySerializer(serializers.Serializer):
more = serializers.SerializerMethodField()
def get_more(self, obj):
return "123"
class InfoSerializer(serializers.ModelSerializer, MySerializer):
class Meta:
model = models.UserInfo
fields = ["id", "name", 'more']
class InfoView(APIView):
def get(self, request):
instance = models.UserInfo.objects.all().first()
ser = InfoSerializer(instance=instance, many=False)
print(type(ser.data), ser.data)
return Response(ser.data)
9.6 序列化数据校验
使用序列化器可以实现对字段数据进行校验。
1.基本校验
在括号内写需要校验的条件,required必填
class DepartSerializer(serializers.Serializer):
title = serializers.CharField(required=True, max_length=20, min_length=6)
order = serializers.IntegerField(required=False, max_value=100, min_value=10)
level = serializers.ChoiceField(choices=[(1, "高级"), (2, "中级")])
class DepartView(APIView):
def post(self, request, *args, **kwargs):
# 1.获取原始数据
print(request.data)
# 2.校验
ser = DepartSerializer(data=request.data)
if ser.is_valid():
print(ser.validated_data)
else:
print(ser.errors)
#ser.is_valid(raise_exception=True) # 成功,抛出异常 raise ValidationError(self.errors)
#print(ser.validated_data)
return Response("成功")
2.内置和正则校验
from django.core.validators import EmailValidator, RegexValidator
from rest_framework import exceptions
class DepartSerializer(serializers.Serializer):
title = serializers.CharField(required=True, max_length=20, min_length=6)
order = serializers.IntegerField(required=False, max_value=100, min_value=10)
level = serializers.ChoiceField(choices=[(1, "高级"), (2, "中级")])
# email = serializers.EmailField()
# email = serializers.CharField(validators=[EmailValidator(message="邮箱格式错误")])
email = serializers.CharField(validators=[RegexValidator(r"\d+", message="邮箱格式错误"), ])
3.钩子校验
使用钩子函数validate,校验某一个字段的钩子函数名为validate_字段名,validate用于所有校验通过后的全局校验。
rom django.core.validators import EmailValidator, RegexValidator
from rest_framework import exceptions
class DepartSerializer(serializers.Serializer):
title = serializers.CharField(required=True, max_length=20, min_length=6)
order = serializers.IntegerField(required=False, max_value=100, min_value=10)
level = serializers.ChoiceField(choices=[(1, "高级"), (2, "中级")])
# email = serializers.EmailField()
# email = serializers.CharField(validators=[EmailValidator(message="邮箱格式错误")])
email = serializers.CharField(validators=[RegexValidator(r"\d+", message="邮箱格式错误"), ])
def validate_email(self, value):
if len(value) > 6:
raise exceptions.ValidationError("字段钩子校验失败")
return value
def validate(self, attrs):
print("validate=", attrs)
# api_settings.NON_FIELD_ERRORS_KEY
raise exceptions.ValidationError("全局钩子校验失败") # non_field_errors
# return attrs
4.使用ModelSerialize校验
class DepartModelSerializer(serializers.ModelSerializer):
more = serializers.CharField(required=True)
class Meta:
model = models.Depart
fields = ["title", "order", "more"]
extra_kwargs = {
"title": {"max_length": 5, "min_length": 1},
"order": {"min_value": 5},
# "count": {"validators": [RegexValidator(r"\d+", message="格式错误")]},
}
class DepartView(APIView):
def post(self, request, *args, **kwargs):
# 1.获取原始数据
# print(request.data)
# 2.校验
ser = DepartModelSerializer(data=request.data)
if ser.is_valid():
print("视图", ser.validated_data)
ser.validated_data.pop("more") #删除非数据库字段
ser.save(count=100) #添加count字段并存储到数据库
else:
print("视图", ser.errors)
# ser = DepartSerializer(data=request.data)
# ser.is_valid(raise_exception=True) # 成功,抛出异常 raise ValidationError(self.errors)
# print(ser.validated_data)
return Response("成功")
5.外键和manytomant字段
外键和manytomany的字段也可以使用钩子,会自动关联获取到值
class UsModelSerializer(serializers.ModelSerializer):
depart_id = serializers.IntegerField()
tags = serializers.IntegerField()
#获取传递的列表或字典,再结合钩子存储到数据库
# mytag = serializers.DictField()
# mytag = serializers.ListField()
class Meta:
model = models.UserInfo
fields = ["name", "age", "gender", "depart_id", "tags"]
def validate_tags(self, value):
print(value)
# return value
# return [999,990]
queryset = models.Tag.objects.filter(id=value)
return queryset #返回给 ser.validated_data的值
class UsView(APIView):
def post(self, request, *args, **kwargs):
# 1.获取原始数据
# print(request.data)
# 2.校验
ser = UsModelSerializer(data=request.data)
if ser.is_valid():
print("视图", ser.validated_data)
# ser.validated_data.pop("tags")
instance = ser.save() #获取当前对象
# instance.id
# instance.name
# instance.age
else:
print("视图", ser.errors)
return Response("成功")
9.7序列化返回值
1.以下返回值包含id,title,order但没有count.count只在校验的时候有,序列化返回时没有
使用场景:
基于read_only和write_only实现用户注册时,不返回用户的密码
2.若输入为性别id,返回展示性别字符串:
class UusModelSerializer(serializers.ModelSerializer):
# v1 = serializers.CharField(source="depart.title", read_only=True)
gender_info=serializers.CharField(source="get_gender_display",read_only=True)
class Meta:
model = models.UserInfo
fields = ["id", "age", "gender", "depart", "gender_info"]
extra_kwargs = {
"id": {"read_only": True},
"gender": {"write_only": True},
}
#
# def get_name(self, obj):
# return 1111
class UusView(APIView):
def post(self, request, *args, **kwargs):
ser = UusModelSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
既要返回id又要返回text,可以使用钩子实现:
class UusModelSerializer(serializers.ModelSerializer):
# v1 = serializers.CharField(source="depart.title", read_only=True)
# gender_info=serializers.CharField(source="get_gender_display",read_only=True)
v1=serializers.SerializerMethodField()
class Meta:
model = models.UserInfo
fields = ["id", "age", "gender", "depart", "v1"]
extra_kwargs = {
"id": {"read_only": True},
"gender": {"write_only": True},
}
#
def get_v1(self, obj):
return {'id': obj.gender,'text':obj.get_gender_display()}
3.获取外键关联的所有数据:可以使用嵌套
class P1ModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Depart
fields = "__all__"
class UusModelSerializer(serializers.ModelSerializer):
v1=P1ModelSerializer(read_only=True,source="depart")
class Meta:
model = models.UserInfo
fields = ["id", "age", "gender", "depart", "v1"]
extra_kwargs = {
"id": {"read_only": True},
"gender": {"write_only": True},
}
class UusView(APIView):
def post(self, request, *args, **kwargs):
ser = UusModelSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
十、 drf分页类
1.PageNumberPagination(BasePagination)
支持 /accounts/?page=4&page_size=100 格式的分页,传入页码和每页条数
2.LimitOffsetPagination(BasePagination)
支持 /accounts/?offset=100&limit=10 格式的分页
支持/accounts/?lastid=10&offset=0&limit=10,找到最后一条数据,往后从offset起选择limit条
10.1 PageNumberPagination
1.自定义PageNumberPagination类
序列化时直接传入分页后得到的queryset
class BlogView(APIView):
authentication_classes = [BlogAuthentication, ]
def get(self, request, *args, **kwargs):
""" 博客列表 """
# 1.读取数据库中的博客信息
queryset = models.Blog.objects.all().order_by("id")
# 2.分页处理得到分页后的->queryset
from rest_framework.pagination import PageNumberPagination
pager = PageNumberPagination()
result = pager.paginate_queryset(queryset, request, self)
# 3.序列化
ser = BlogSerializers(instance=result, many=True)
# 4.获取序列化结果 or 分页返回处理
response = pager.get_paginated_response(ser.data)
return response
2.get_paginated_response返回值
get_paginated_response包括上一页下一页,总数和data,并封装了返回。
class BlogView(APIView):
# 获取博客列表
def get(self,*args,**kwargs):
#1.查询博客列表
queryset=models.Blog.objects.all().order_by("-id")
from rest_framework.pagination import PageNumberPagination
pager = PageNumberPagination()
result = pager.paginate_queryset(queryset, request, self)
# 2.序列化
ser = BlogSerializers(instance=result, many=True)
# 3.返回
# context = {"code": 1000, 'data': ser.data}
# return Response(context)
response=pager.get_paginated_response(ser.data)
return response
10.2 LimitOffsetPagination
跟上面同样使用方式
可以传入最大id或者查询最新id,再结合该类进行查询:
class BlogView(APIView):
authentication_classes = [BlogAuthentication, ]
def get(self, request, *args, **kwargs):
""" 博客列表 """
# 1.读取数据库中的博客信息
queryset = models.Blog.objects.all().order_by("-id")
# ?max_id=1
# ?min_id=13
max_id = request.query_params.get("max_id")
if max_id:
queryset = queryset.filter(id__lt=max_id)
# 2.分页处理得到分页后的->queryset
from rest_framework.pagination import LimitOffsetPagination
pager = LimitOffsetPagination()
result = pager.paginate_queryset(queryset, request, self)
# 3.序列化
ser = BlogSerializers(instance=result, many=True)
# 4.获取序列化结果 or 分页返回处理
response = pager.get_paginated_response(ser.data)
return response
十一、 drf视图类
11.1 GenericAPIView
GenericAPIView
继承APIView,在APIView的基础上又增加了一些功能。例如:get_queryset
、get_object
等。
实际在开发中一般不会直接继承它,他更多的是担任 中间人
的角色,为子类提供公共功能。
class MyFilterBackend1(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
# xx = request.query_params.get("xx")
# queryset = queryset.filter(status=xx)
return queryset
class MyFilterBackend2(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
return queryset
class DemoDetailView(GenericAPIView):
queryset = models.UserInfo.objects
# filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
filter_backends = [MyFilterBackend1, MyFilterBackend2]
def get(self, request, pk):
print(pk) # pk=主键 id=xx
# 1.取数据
# obj = models.UserInfo.objects.filter(pk=pk).first()
# obj = models.UserInfo.objects.get(id=10) # id=10那条数据必须找到 & 必须只有一条
# obj = models.UserInfo.objects.filter(status=2).get(id=10) # id=10那条数据必须找到 & 必须只有一条
# obj = models.UserInfo.objects.filter(status=请求参数xx).get(id=10) # id=10那条数据必须找到 & 必须只有一条
# 取数据 + 权限校验 has_object_permission
obj = self.get_object()
# 2.序列化
# ser = DemoSerializer(instance=obj, many=False)
# ser.data
ser = self.get_serializer(instance=obj, many=True)
return Response(ser.data)
11.2 GenericViewSet
他的功能就是将GenericAPIView,ViewSetMixin`的两个类的功能继承到一起。
GenericAPIView
,将数据库查询、序列化类的定义提取到类变量中,便于后期处理。ViewSetMixin
,将 get/post/put/delete 等方法映射到 list、create、retrieve、update、partial_update、destroy方法中,让视图不再需要两个类。
# urls.py
from django.urls import path, re_path, include
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view({"get":"list","post":"create"})),
path('api/users/<int:pk>/', views.UserView.as_view({"get":"retrieve","put":"update","patch":"partial_update","delete":"destory"})),
]
# views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.response import Response
class UserView(GenericViewSet):
# 认证、权限、限流等
queryset = models.UserInfo.objects.filter(status=True)
serializer_class = 序列化类
def list(self, request):
# 业务逻辑:查看列表
queryset = self.get_queryset()
ser = self.get_serializer(intance=queryset,many=True)
print(ser.data)
return Response({"code": 0, 'data': "..."})
def create(self, request):
# 业务逻辑:新建
return Response({'code': 0, 'data': "..."})
def retrieve(self, request,pk):
# 业务逻辑:查看某个数据的详细
return Response({"code": 0, 'data': "..."})
def update(self, request,pk):
# 业务逻辑:全部修改
return Response({'code': 0, 'data': "..."})
def partial_update(self, request,pk):
# 业务逻辑:局部修改
return Response({'code': 0, 'data': "..."})
def destory(self, request,pk):
# 业务逻辑:删除
return Response({'code': 0, 'data': "..."})
11.3五大类(ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin,DestroyModelMixin)
在drf的为我们提供好了5个用于做 增、删、改(含局部修改)、查列表、查单个数据的5个类(需结合 GenericViewSet
使用)。
在这个5个类中已帮我们写好了 list
、create
、retrieve
、update
、partial_update
、destory
方法,我们只需要在根据写 类变量:queryset、serializer_class即可。
# urls.py
from django.urls import path, re_path, include
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view({"get":"list","post":"create"})),
path('api/users/<int:pk>/', views.UserView.as_view({"get":"retrieve","put":"update","patch":"partial_update","delete":"destroy"})),
]
# views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import (
ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin,DestroyModelMixin, ListModelMixin
)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__"
class UserView(CreateModelMixin,RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin,ListModelMixin,GenericViewSet):
# 认证、权限、限流等
queryset = models.UserInfo.objects.filter(status=True)
serializer_class = UserSerializer #序列化类
1.查询
示例1: 查列表,或查询指定
# urls.py
from django.urls import path
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view({"get": "list"})),
path('api/users/<int:pk>/', views.UserView.as_view({"get": "retrieve"})),
]
# views.py
from rest_framework import serializers
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(
source="get_level_display",
read_only=True
)
extra = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.UserInfo
fields = ["username", "age", "email", "level_text", "extra"]
def get_extra(self, obj):
return 666
class UserView(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
2.新增
示例2:新增
# urls.py
from django.urls import path
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view({"get": "list", "post": "create"})),
path('api/users/<int:pk>/', views.UserView.as_view({"get": "retrieve"})),
]
# views.py
from rest_framework import serializers
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(
source="get_level_display",
read_only=True
)
extra = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.UserInfo
fields = ["username", "age", "email", "level_text", "extra"]
def get_extra(self, obj):
return 666
class UserView(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin, GenericViewSet):
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
def perform_create(self, serializer):
""" 序列化:对请求的数据校验成功后,执行保存。"""
serializer.save(depart_id=1, password="123") #有其他数据库必填字段重写该方法写入即可
3.更新,删除
示例3:更新与局部更新
全部字段都更新使用PUT方法,更新局部字段使用PATCH方法
# urls.py
from django.urls import path
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view(
{"get": "list", "post": "create"}
)),
path('api/users/<int:pk>/', views.UserView.as_view(
{"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"}
)),
]
# views.py
from rest_framework import serializers
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(
source="get_level_display",
read_only=True
)
extra = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.UserInfo
fields = ["username", "age", "email", "level_text", "extra"]
def get_extra(self, obj):
return 666
class UserView(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericViewSet):
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
def perform_create(self, serializer):
""" 序列化:对请求的数据校验成功后,执行保存。"""
serializer.save(depart_id=1, password="123")
def perform_update(self, serializer):
serializer.save()
def perform_destroy(self, instance):
instance.delete()
4.序列化器切换
**自定义序列化器:**当多个类写在一起时,想要使用不同的序列化器,就需要重写GenericAPIView中get_serializer_class
方法,根据需求自定义返回哪个序列化类。
如:查询列表和查询某个数据,根据是否传递pk来判断
class BlogView(ListModelMixin, RetrieveModelMixin, GenericViewSet): # (ViewSetMixin, generics.GenericAPIView)
queryset = models.Blog.objects.all()
serializer_class = BlogSerializer
# pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
def get_serializer_class(self):
# 两个请求哪里不同?查询列表没有pk,查询具体某个数据传递了pk
pk = self.kwargs.get('pk')
if pk:
return self.serializer_class
return self.serializer_class
5.ModelViewSet
每次都有增删改查,继承没一个比较麻烦,直接继承ModelViewSet
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass
十二、drf路由
12.1 SimpleRouter
在之前进行drf开发时,对于路由我们一般进行两种配置:
-
视图继承APIView
from django.urls import path from app01 import views urlpatterns = [ path('api/users/', views.UserView.as_view()), # APIView ]
-
视图继承
ViewSetMixin
(GenericViewSet、ModelViewSet)from django.urls import path, re_path, include from app01 import views urlpatterns = [ path('api/users/', views.UserView.as_view({"get":"list","post":"create"})), path('api/users/<int:pk>/', views.UserView.as_view({"get":"retrieve","put":"update","patch":"partial_update","delete":"destory"})), ]
对于这种形式的路由,drf中提供了更简便的方式:
使用SimpleRouter类,会根据view中继承的五大类,自动生成对应的路由,就不需要自己再写五大类对应的路由参数
from rest_framework import routers
from app01 import views
router = routers.SimpleRouter()
router.register(r'api/users', views.UserView)
urlpatterns = [
# 其他URL
# path('xxxx/', xxxx.as_view()),
]
urlpatterns += router.urls
-
也可以利用include,给URL加前缀:
from django.urls import path, include from rest_framework import routers from app01 import views router = routers.SimpleRouter() router.register(r'users', views.UserView) urlpatterns = [ path('api/', include((router.urls, 'app_name'), namespace='instance_name')), # 其他URL # path('forgot-password/', ForgotPasswordFormView.as_view()), ]
12.2 路由扩展
from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action
class XXXModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__"
class XXXView(ModelViewSet):
queryset = models.UserInfo.objects.all()
serializer_class = XXXModelSerializer
#访问api/user/yyy/123/xxx时会进入get_password方法,若不指定url_path,则访问api/user/get_password时进入
@action(detail=False, methods=['get'], url_path="yyy/(?P<xx>\d+)/xxx")
def get_password(self, request, xx, pk=None):
print(xx)
return Response("...")
#访问api/user/1/yyy/123/xxx时会进入set_password方法,若不指定url_path,则访问api/user/1/set_password时进入
@action(detail=True, methods=['get'], url_path="yyy/(?P<xx>\d+)/xxx")
def set_password(self, request, xx, pk=None):
print(xx)
return Response("...")
detail:为True时,再当前路由后面带一个id再进行扩展
url_path:指定扩展url的格式
十三、drf筛选器(条件搜索)
如果某个API需要传递一些条件进行搜索,其实就在是URL后面通过GET传参即可,例如:
/api/users?age=19&category=12
在drf中也有相应组件可以支持条件搜索。
13.1 自定义Filter(推荐)
将固定的筛选使用 queryset = models.Blog.objects.all()
如果根据url参数动态筛选时,使用自定义Filter
# urls.py
from django.urls import path
from app01 import views
urlpatterns = [
path('api/users/', views.UserView.as_view(
{"get": "list", "post": "create"}
)),
path('api/users/<int:pk>/', views.UserView.as_view(
{"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"}
)),
]
# views.py
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from rest_framework.filters import BaseFilterBackend
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(
source="get_level_display",
read_only=True
)
extra = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.UserInfo
fields = ["username", "age", "email", "level_text", "extra"]
def get_extra(self, obj):
return 666
class Filter1(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
age = request.query_params.get('age')
if not age:
return queryset
return queryset.filter(age=age)
class Filter2(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
user_id = request.query_params.get('id')
if not user_id:
return queryset
return queryset.filter(id__gt=user_id)
class UserView(ModelViewSet):
filter_backends = [Filter1, Filter2]
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
def perform_create(self, serializer):
""" 序列化:对请求的数据校验成功后,执行保存。"""
serializer.save(depart_id=1, password="123")
13.2 第三方Filter
在drf开发中有一个常用的第三方过滤器:DjangoFilterBackend。
pip install django-filter
注册app:
INSTALLED_APPS = [
...
'django_filters',
...
]
视图配置和应用(示例1):在 filterset_fields = [“id”, “age”, “email”]定义后,直接能判断字段=值的过滤,如id=1&age=12
# views.py
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(
source="get_level_display",
read_only=True
)
extra = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.UserInfo
fields = ["username", "age", "email", "level_text", "extra"]
def get_extra(self, obj):
return 666
class UserView(ModelViewSet):
filter_backends = [DjangoFilterBackend, ]
filterset_fields = ["id", "age", "email"]
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
def perform_create(self, serializer):
""" 序列化:对请求的数据校验成功后,执行保存。"""
serializer.save(depart_id=1, password="123")
视图配置和应用(示例2):
如果要判断大于、以什么开头等过滤条件,则定义class MyFilterSet(FilterSet)
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from django_filters import FilterSet, filters
from app01 import models
class UserModelSerializer(serializers.ModelSerializer):
level_text = serializers.CharField(
source="get_level_display",
read_only=True
)
depart_title = serializers.CharField(
source="depart.title",
read_only=True
)
extra = serializers.SerializerMethodField(read_only=True)
class Meta:
model = models.UserInfo
fields = ["id", "username", "age", "email", "level_text", "extra", "depart_title"]
def get_extra(self, obj):
return 666
class MyFilterSet(FilterSet):
depart = filters.CharFilter(field_name="depart__title", lookup_expr="exact")
min_id = filters.NumberFilter(field_name='id', lookup_expr='gte')
class Meta:
model = models.UserInfo
fields = ["min_id", "depart"]
class UserView(ModelViewSet):
filter_backends = [DjangoFilterBackend, ]
filterset_class = MyFilterSet
queryset = models.UserInfo.objects.all()
serializer_class = UserModelSerializer
def perform_create(self, serializer):
""" 序列化:对请求的数据校验成功后,执行保存。"""
serializer.save(depart_id=1, password="123")
class MyFilterSet(FilterSet):
# /api/users/?min_id=2 -> id>=2
min_id = filters.NumberFilter(field_name='id', lookup_expr='gte')
# /api/users/?name=wupeiqi -> not ( username=wupeiqi )
name = filters.CharFilter(field_name="username", lookup_expr="exact", exclude=True)
# /api/users/?depart=xx -> depart__title like %xx%
depart = filters.CharFilter(field_name="depart__title", lookup_expr="contains")
# /api/users/?token=true -> "token" IS NULL
# /api/users/?token=false -> "token" IS NOT NULL
token = filters.BooleanFilter(field_name="token", lookup_expr="isnull")
# /api/users/?email=xx -> email like xx%
email = filters.CharFilter(field_name="email", lookup_expr="startswith")
# /api/users/?level=2&level=1 -> "level" = 1 OR "level" = 2(必须的是存在的数据,否则报错-->内部有校验机制)
# level = filters.AllValuesMultipleFilter(field_name="level", lookup_expr="exact")
level = filters.MultipleChoiceFilter(field_name="level", lookup_expr="exact", choices=models.UserInfo.level_choices)
# /api/users/?age=18,20 -> age in [18,20]
age = filters.BaseInFilter(field_name='age', lookup_expr="in")
# /api/users/?range_id_max=10&range_id_min=1 -> id BETWEEN 1 AND 10
range_id = filters.NumericRangeFilter(field_name='id', lookup_expr='range')
# /api/users/?ordering=id -> order by id asc
# /api/users/?ordering=-id -> order by id desc
# /api/users/?ordering=age -> order by age asc
# /api/users/?ordering=-age -> order by age desc
ordering = filters.OrderingFilter(fields=["id", "age"])
# /api/users/?size=1 -> limit 1(自定义搜索)
size = filters.CharFilter(method='filter_size', distinct=False, required=False)