rest framework介绍 (CBV(class base views))
在开发REST API的视图中,虽然每个视图具体操作的数据不同,但增、删、改、查的实现流程基本套路化,所以这部分代码也是可以复用简化编写的:
增:校验请求数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回
删:判断要删除的数据是否存在 -> 执行数据库删除
改:判断要修改的数据是否存在 -> 校验请求的数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回
查:查询数据库 -> 将数据序列化并返回
解析url中的 as_view()
url(r'^publishers/$', views.PublishViewSet.as_view(),name="publish_list"),
继承
APIView继承自View,APIView中有as_view方法,方法中会执行下面这段代码
执行了父类的as_view方法,得到view函数,执行dispatch方法(一切的开始)
序列化
序列化用于对用户请求数据进行验证和数据进行序列化
第一种表示方法——Serializers
from rest_framework import serializers from app01 import models from rest_framework.views import APIView from rest_framework.response import Response class BookSerializer(serializers.Serializer): title = serializers.CharField(max_length=32) price = serializers.IntegerField() pub_date = serializers.DateField() class TestView(APIView): def get(self, request, *args, **kwargs): # 序列化,将数据库查询字段序列化为字典 obj = models.Book.objects.all() ser = BookSerializer(obj, many=True) # 如果不是queryset,就不用加many=True # obj = models.Book.objects.all().first() # ser = BookSerializer(obj, many=False) return Response(ser.data) def post(self, request, *args, **kwargs): # 验证,对请求发来的数据进行验证 ser = BookSerializer(data=request.data) if ser.is_valid(): print(ser.validated_data) else: print(ser.errors) return Response('Post请求')
第二种表示方法——ModelSerializers:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers from app01 import models class BookSerializer(serializers.ModelSerializer): class Meta: model = models.Book fields = "__all__" # 全部 # fields = ['title', 'price'] 只查两项 # depth = 2 # 查询深度 # exclude = ('price',) 除了price不查询 class TestView(APIView): def get(self, request, *args, **kwargs): # 序列化,将数据库查询字段序列化为字典 obj = models.Book.objects.all() ser = BookSerializer(obj, many=True) # 如果不是queryset,就不用加many=True # obj = models.Book.objects.all().first() # ser = BookSerializer(obj, many=False) return Response(ser.data) def post(self, request, *args, **kwargs): # 验证,对请求发来的数据进行验证 ser = BookSerializer(data=request.data) if ser.is_valid(): print(ser.validated_data) ser.save() # create 方法 return Response(ser.data) else: print(ser.errors) return Response('Post请求')
特殊取值(取user_type对应的中文)
class UserInfo(models.Model): user_type_choices = ( (1,'普通用户'), (2,'VIP'), (3,'SVIP'), ) user_type = models.IntegerField(choices=user_type_choices)
class UserInfoSerializer(serializers.Serializer): xxxxx = serializers.CharField(source="user_type") # row.user_type source将xxxx对应为user_type oooo = serializers.CharField(source="get_user_type_display") # row.get_user_type_display() 数据:[{"xxxx":1,"oooo":"普通用户"}]
多表查询
一对多查询通过source实现
from django.db import models class UserGroup(models.Model): title = models.CharField(max_length=32) class UserInfo(models.Model): user_type_choices = ( (1,'普通用户'), (2,'VIP'), (3,'SVIP'), ) user_type = models.IntegerField(choices=user_type_choices) username = models.CharField(max_length=32,unique=True) password = models.CharField(max_length=64) group = models.ForeignKey("UserGroup") roles = models.ManyToManyField("Role") class UserToken(models.Model): user = models.OneToOneField(to='UserInfo') token = models.CharField(max_length=64) class Role(models.Model): title = models.CharField(max_length=32)
from rest_framework.views import APIView from rest_framework import serializers from . import models from rest_framework.response import Response class UserInfoSerializers(serializers.Serializer): user_type = serializers.IntegerField() user_type_choices = serializers.CharField(source="get_user_type_display") group = serializers.CharField(source="group.title")
多对多通过自定义实现
class UserInfoSerializers(serializers.Serializer): user_type = serializers.IntegerField() user_type_choices = serializers.CharField(source="get_user_type_display") group = serializers.CharField(source="group.title") rls = serializers.SerializerMethodField() def get_rls(self, obj): # get_rls和上边rls要对应 ret = [] for item in obj.roles.all(): print(item) ret.append({"id": item.id, "title": item.title}) return ret
在视图中生成url
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^userinfo/$', views.UserInfoView.as_view(), ), url(r'^group/(?P<pk>d+)$', views.GroupView.as_view(), name='gp'),
from rest_framework.views import APIView from rest_framework import serializers from . import models from rest_framework.response import Response from django.http import HttpResponse import json class RoleSerializers(serializers.ModelSerializer): class Meta: model = models.Role fields = "__all__" class UserInfoSerializers(serializers.Serializer): group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='pk') user_type = serializers.IntegerField() user_type_choices = serializers.CharField(source="get_user_type_display") # group = serializers.CharField(source="group.id") rls = serializers.SerializerMethodField() def get_rls(self, obj): ret = [] for item in obj.roles.all(): print(item) ret.append({"id": item.id, "title": item.title}) return ret class UserInfoView(APIView): def get(self, request): obj = models.UserInfo.objects.all() ser = UserInfoSerializers(obj, many=True, context={'request': request}) return Response(ser.data) class GroupSerializers(serializers.Serializer): class Meta: model = models.UserGroup fields = '__all__' class GroupView(APIView): def get(self, request, *args, **kwargs): pk = kwargs.get('pk') obj = models.UserGroup.objects.filter(pk=pk).first() ser = GroupSerializers(obj, many=False) ret = json.dumps(ser.data, ensure_ascii=False) return HttpResponse(ret)
group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='pk')
lookup_url_kwarg='pk'是和url中的(?P<pk>d+)$'的pk对应,lookup_field='group_id'是取group的id值
ser = UserInfoSerializers(obj, many=True, context={'request': request}) context是必须添加的
序列化验证钩子(validate+校验字段名)
class RoleSerializers(serializers.ModelSerializer): class Meta: model = models.Role fields = "__all__" # 局部钩子 def validate_title(self, attrs): if attrs.startswith("666"): raise ValidationError("不能以666开头") return attrs # 全局钩子 def validate(self, attrs): return attrs
视图
a. GenericAPIView(没什么用)
from rest_framework import serializers from . import models from rest_framework.generics import GenericAPIView from rest_framework.response import Response class RoleSerializers(serializers.ModelSerializer): class Meta: model = models.Role fields = '__all__' class RoleView(GenericAPIView): queryset = models.Role.objects.all() serializer_class = RoleSerializers def get(self, request, *args, **kwargs): # 获取数据 obj = self.get_queryset() # 序列化 ser = self.get_serializer(obj, many=True) return Response(ser.data)
b. GenericViewSet
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^role/$', views.RoleView.as_view({'get': 'list'}), ), # 和view里边的list方法做对应关系
class RoleSerializers(serializers.ModelSerializer): class Meta: model = models.Role fields = '__all__' class RoleView(GenericViewSet): queryset = models.Role.objects.all() serializer_class = RoleSerializers def list(self, request, *args, **kwargs): # 获取数据 obj = self.get_queryset() # 序列化 ser = self.get_serializer(obj, many=True) return Response(ser.data)
c. ModelViewSet
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^role/$', views.RoleView.as_view({'get': 'list', 'post': 'create'}), ), url(r'^role/(?P<pk>)d+/$', views.RoleView.as_view({'get': 'retrieve', 'delete': 'destroy', 'put': 'update', 'patch': 'partial_update'}), ), ]
class RoleView(ModelViewSet): queryset = models.Role.objects.all() serializer_class = RoleSerializers
a. 增删改查 ModelViewSet
b. 增删 CreateModelMixin,DestroyModelMixin GenericViewSet
c. 复杂逻辑 GenericViewSet 或 APIView
路由
会自动生成增删改查等url
from django.conf.urls import url, include from django.contrib import admin from app01 import views from rest_framework import routers router = routers.DefaultRouter() router.register(r'role', views.RoleView) urlpatterns = [ url(r'^', include(router.urls)), ]
认证
a.用户url传入的token认证
from rest_framework.authentication import BasicAuthentication from rest_framework import exceptions class MyAuthentication(BasicAuthentication): def authenticate(self, request): token = request.GET.get('token') token_obj = models.UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed('Token认证失败') else: return token_obj.username, token_obj.token # 验证通过返回元祖,第一个参数和request.user是对应的,所以最好是用户名 class RoleView(ModelViewSet): authentication_classes = [MyAuthentication, ] queryset = models.Role.objects.all() serializer_class = RoleSerializers
b.用户登录添加token
class RoleView(ModelViewSet): authentication_classes = [] queryset = models.Role.objects.all() serializer_class = RoleSerializers def post(self, request, *args, **kwargs): res = {'state_code': 1000, 'msg': None} try: name = request._request.POST.get('username') pwd = request._request.POST.get('password') user = models.UserInfo.objects.filter(username=name, password=pwd).first() if not user: res["state_code"] = 1001 # 错误状态码 res["msg"] = "用户名或者密码错误" else: # 为用户创建token token = md5(name) # 存在就更新,否则就创建 models.UserToken.objects.update_or_create(user=user, defaults={'token': token}) res['token'] = token except Exception as e: res['state_code'] = 1002 res['msg'] = '请求异常' return Response(res)
BasicAuthentication、SessionAuthentication、TokenAuthentication、JSONWebTokenAuthentication 都属于认证都继承BaseAuthentication,方法类似
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', # 基本认证 'rest_framework.authentication.SessionAuthentication', # session认证 ) }
权限
class MyPermission(BasePermission): def has_permission(self, request, view): message = '只有SVIP可以访问' if request.user != 3: return False return True # 局部 permission_classes = [MyPermission, ]
频率
from rest_framework.throttling import SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): # 配置文件中进行配置 scope = 'Luffy' def get_cache_key(self, request, view): return self.get_ident(request) class UserThrottle(SimpleRateThrottle): scope = 'LuffyUser' 局部 throttle_classes = [VisitThrottle, ]
REST_FRAMEWORK = { "DEFAULT_THROTTLE_RATES": { "Luffy": '3/m', "LuffyUser": '10/m', } }
版本
a.url的get传参方式
/user?version=v1
REST_FRAMEWORK = { 'DEFAULT_VERSION': 'v1', # 默认版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本 'VERSION_PARAM': 'version' # URL中获取值的key }
from rest_framework.versioning import QueryParameterVersioning class TestView(APIView): versioning_class = QueryParameterVersioning def get(self, request, *args, **kwargs): # 获取版本 print(request.version) # 获取版本管理的类 print(request.versioning_scheme) # 反向生成URL reverse_url = request.versioning_scheme.reverse('test', request=request) print(reverse_url) return Response('GET请求,响应内容')
b.基于url正则表达式
/v1/user/
REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning' 'DEFAULT_VERSION': 'v1', # 默认版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本 'VERSION_PARAM': 'version' # URL中获取值的key }
class TestView(APIView): # 局部 versioning_class = URLPathVersioning def get(self, request, *args, **kwargs): # 获取版本 print(request.version) # 获取版本管理的类 print(request.versioning_scheme) # 反向生成URL reverse_url = request.versioning_scheme.reverse('test', request=request) print(reverse_url)
urlpatterns = [ url(r'^(?P<version>[v1|v2]+)/test/', TestView.as_view(), name='test'), ]
解析器
用于处理不同请求头数据
REST_FRAMEWORK = { "DEFAULT_PARSER_CLASSES":['rest_framework.parsers.JSONParser','rest_framework.parsers.FormParser'] }
class ParserView(APIView): # parser_classes = [JSONParser,FormParser,] """ JSONParser:表示只能解析content-type:application/json头 FormParser:表示只能解析content-type:application/x-www-form-urlencoded头 """ def post(self,request,*args,**kwargs): """ 允许用户发送JSON格式数据 a. content-type: application/json b. {'name':'alex',age:18} :param request: :param args: :param kwargs: :return: """ """ 1. 获取用户请求 2. 获取用户请求体 3. 根据用户请求头 和 parser_classes = [JSONParser,FormParser,] 中支持的请求头进行比较 4. JSONParser对象去请求体 5. request.data """ print(request.data) return HttpResponse('ParserView')
dispatch