零,DRF框架预备知识
APIView与View的区别
APIView继承了View
csrf的豁免
重现封装request对象
原生的request赋值给了request._request
request.query_params封装了原生的request.GET
request.data封装了除GET外的所有信息(request.POST,request.files)
原生的request为WSGIRequest的实例化对象
响应对象Response
携带HTTP表中状态码
做模板的渲染
一,restful规范
REST风格
表述状态转移:web交换方案,前后端的数据传输的设计思想
资源的概念:在web中只要又被引用的必要都是资源
URI
URI:同一资源标识符
URL:统一资源定位符,URI的子集
统一资源接口
根据HTTP请求方式的不同对资源进行不同的操作
遵循HTTP请求的语义
资源的表述
前后端的传输叫资源的表述
传输的不是资源的本身,而是资源的某一种表述形式
资源的状态
前端展示的叫资源的状态
通过超链接的指引告诉用户接下来有哪些资源状态可以进入
核心思想
面向资源编程
每个url就是资源的体现,不体现操作,url命名尽量用名词不要用动词
根据HTTP请求方式的不同对资源进行不同的操作
URL体现:
版本
htttps://v1.xxx.com
https://xxx.com/v3
体现是否为API接口
https://v1.xxx.com/api
过滤信息
https://v1.xxx.com?page=1
尽量使用HTTPS
返回值的体现(响应请求)
携带状态码
1xx:信息,服务器收到请求,需要请求者继续执行操作
2xx:成功,操作被成功接收并处理
3xx:重定向,需要进一步的操作以完成请求
4xx:客户端错误,请求包含语法错误或无法完成请求
5xx:服务器错误,服务器在处理请求的过程中发生了错误
携带错误信息
返回值
get 返回查看的所有或单条信息
post 返回新增的那条数据
put 返回更新数据
patch 局部更新,返回更新的那条数据
delete 返回值为空
Hypermedia API
如果遇到需要跳转的情况 携带跳转接口的URL
ret = { code: 1000, data:{ id:1, name:'小强', depart_id:http://www.baidu.com/api/v1/depart/8/ } }
二,序列化组件
1、序列化
实现流程
1.如果设置了many=True
2.把queryset当成可迭代对象去循环,得到每个模型对象
3.把每个模型对象放入序列化器进行序列化
4.进行字段匹配,匹配上的字段进行序列化,匹配不上的丢弃
5.序列化的时候必须满足序列化的所有字段要求
声明一个序列化器
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) # required=False 忽略校验 title = serializers.CharField(max_length=32) pub_time = serializers.DateField()
在视图中引用我们定义的序列化器,序列化我们的queryset数据
ser_obj = BookSerializer(queryset, many=True) # many=True 支持(当成)可迭代对象,循环遍历并序列化 ser_obj = BookSerializer(models_obj) # 同样支持单个模型对象的序列化
ser_obj.validated_data # 校验通过的数据
return Response(ser_obj.data)
2、反序列化
获取前端传过来的数据
用序列化器进行校验
BookSerializer(data=request.data) if ser_obj.is_valid(): ser_obj.save() # 调用序列化器中的create方法操作orm创建新对象 return Response(ser_obj.data) else: return Response
3、序列化以及反序列化的时候字段类型不统一的情况
-- required=False # 对字段取消校验 -- read_only=True # 仅序列化是进行校验 -- write_only=True # 仅反序列化是校验
4、ModeSerializer 帮我们实现create以及update方法
class BookSerializer(serilalizers.ModelSerializer) # SerializerMethodField方法字段,会将钩子方法的返回值给字段 text = serializers.SerializerMethodField(read_only=True) class Meta: model = Book # 模型类 fields = "__all__" / ["",""] exclude = [""] # 排除某些字段 depth = 1 # 根据你的外键关系找几层,会让你所有的外键变成read_only = True extra_kwargs = { "字段名称":{参数:值} # 为自动生成的字段添加参数 } # SerializerMethodField的钩子方法定义 def get_字段名称(self,obj): obj 是我们循环序列化的每个模型对象 return 自己想要的数据
4.字段的校验方法
多个字段的校验方法,优先级为 低
def validate(self,attrs): # attrs 前端传过来的所有的数据组成的字典 raise serializers.ValidationError("xxxx") return value
单个字段的校验方法,优先级为 中
def validate_字段名(self,value): # value字段的值 raise serializers.ValidationError("xxxx") return value
自定义校验方法,优先级为 高
# 字段中添加validators指定校验方法 title = serializers.CharField(max_length=32,validators=[my_validate]) def my_validate(value): raise serializers.ValidationError("xxxx") return value
三,视图组件
视图的封装
class GenericAPIView(APIView): query_set = None serializer_class = None def get_query_set(self): return self.query_set # 从对象属性开始找 def get_serializer(self, *args, **kwargs): # 序列化器实例化时,以传参的方式执行 return self.serializer_class(*args, **kwargs) class RetrieveModelMixin(object): def retrieve(self, request, pk): book_obj = self.get_query_set().filter(pk=pk).first() ser_obj = self.get_serializer(book_obj) return Response(ser_obj.data) class ListModelMixin(object): def list(self, request): # print(self.action_map) # actions:{'get': 'list', 'post': 'create'} ser_obj = self.get_serializer(self.get_query_set(), many=True) return Response(ser_obj.data) class CreateModelMixin(object): def create(self, request): ser_obj = self.get_serializer(data=request.data) if ser_obj.is_valid(): ser_obj.save() return Response(ser_obj.data) # print(ser_obj.errors) return Response(ser_obj.errors) class UpdateModelMixin(object): def update(self, request, pk): book_obj = self.get_query_set().filter(pk=pk).first() ser_obj = self.get_serializer(instance=book_obj, data=request.data,partial=True) # 不用每个字段强制都要传值 if ser_obj.is_valid(): ser_obj.save() return Response(ser_obj.data) return Response(ser_obj.errors) class DestoryModelMixin(object): def destory(self, request, pk): book_obj = self.get_query_set().filter(pk=pk).first() if book_obj: book_obj.delete() return Response("") return Response("删除的对象不存在") class ListCreateModeMixin(GenericAPIView, ListModelMixin, CreateModelMixin): pass class RetrieveUpdateDestroyModelMixn(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestoryModelMixin): pass # 继承了ViewSetMixin并使用了它的as_view方法 class ModelViewSet(ViewSetMixin, ListCreateModeMixin, RetrieveUpdateDestroyModelMixn): pass # 第一种CBV class BookListAPIView(ListCreateModeMixin): query_set = Book.objects.all() serializer_class = BookSerializer def get(self, request): return self.list(request) def post(self, request): return self.create(request) class BookEditAPIView(RetrieveUpdateDestroyModelMixn): query_set = Book.objects.all() serializer_class = BookSerializer def get(self, request, id): return self.retrieve(request, id) def put(self, request, id): return self.update(request, id) def delete(self, request, id): return self.destory(request, id) # 第二种,解耦后的CBV class BookModelView(ModelViewSet): query_set = Book.objects.all() # 使用路由系统后,需要queryset serializer_class = BookSerializer # queryset 如果重写视图的话,使用queryset作为参数会被框架放入缓存 # 防止数据不更新,应用调用all()方法重新获取数据 # self.get_queryset() # return self.queryset.all() # 第三种,直接继承框架已定义的ModelViewSet类, from rest_framework.viewsets import ModelViewSet class BookModelView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer class PublisherModelView(ModelViewSet): queryset = Publisher.objects.all() serializer_class = PublisherSerializer class AuthorModelView(ModelViewSet): queryset = Author.objects.all() serializer_class = AuthorSerializer
注意
# 如果不利用ViewSetMixin的as_view方法,对self.get=self.list,如此类推赋值,而是重新的ModelViewSet的话,路由的as_view()方法需要手动穿参,重新指定请求的执行方法 urlpatterns = [ url(r'^list/$', views.BookModelView.as_view({'get':'list','post':'create'})), # 用新的类来处理,由于没有get,post方法, url(r'^list/(?P<id>d+)/$', views.BookModelView.as_view({'get':'retrieve','put':'update','delete':'destroy'})), ]
四,路由组件
# 导入 from rest_framwork.routers import DefaultRouter # 实例化 router = DefaultRouter() # 注册 router.register("list",views.BookModelView) # 默认生成的路由都自带参数(pk),所以视图中的方法也要带参数pk # actions={'get': 'list', 'post': 'create'} 路由系统自动调用as_view({'get': 'list', 'post': 'create'}) # 把默认生成的路由注册 urlpattrens += router.urls # 默认生成的路由都是带参数的!! # ^book/ ^list/$ [name='book-list'] # ^book/ ^list.(?P<format>[a-z0-9]+)/?$ [name='book-list'] # ^book/ ^list/(?P<pk>[^/.]+)/$ [name='book-detail'] # ^book/ ^list/(?P<pk>[^/.]+).(?P<format>[a-z0-9]+)/?$ [name='book-detail'] # ^book/ ^$ [name='api-root'] # ^book/ ^.(?P<format>[a-z0-9]+)/?$ [name='api-root']
五,版本控制组件
# 实现一个版本控制类 from rest_framework.versioning import BaseVersioning # 框架提供了版本控制组件 class MyVersion(BaseVersioning): def determine_version(self,request,*args,**kwargs): # 拿版本号 version = request.query_params.get("version","v1") return version # 源码: version,scheme = self.determine_version(request,*args,**kwargs) # 我们配置的版本控制类一定要有determine_version方法 request.version,request.versioning_scheme = version,scheme # 这个方法的返回值应该是版本号 赋值给request.version def determine_version(self,request,*args,**kwargs): if self.versioning_class is None: return (None,None) # scheme 为我们自己配置的版本控制类的实例化对象 scheme = self.versioning_class() # scheme是我们配置的版本控制类的实例化对象 赋值给了request.versioning_scheme return (scheme.determine_version(request,*args,**kwargs),scheme)
# 在视图
class VersionView(APIView):
def get(self,request,version):
print(request._reuqest.body)
print(request.version)
print(request.versioning_scheme)
if request.verison == 'v1':
return Response('当前版本号为v1')
elif request.version == 'v2':
return Response('当前版本号为v2')
return Response('当前版本不合法')
# 需要在setting中注册
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning' # 对应的版本控制类
}
# 其他几种DRF自带的版本控制类 AcceptHeaderVersioning # Accept: application/json; version=1.0 在Accept头附带版本信息 URLPathVersioning # 在URL中附带版本信息,url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'), NamespaceVersioning # url(r'^v1/', include('users.urls', namespace='v1')), 在名称空间附带版本信息 HostNameVersioning # Host: v1.example.com 在域名附带版本信息 QueryParameterVersioning # 在url参数GET /something/?version=0.1 HTTP/1.1附带版本信息
六,认证控制组件
from rest_framework.authentication import BaseAuthentication from AuthDemo.models import UserInfo from rest_framework.exceptions import AuthenticationFailed # 认证控制类 class MyAuth(object): def authenticate(self,request): # 获取当前前端携带的token # 对对比这个token师傅合法 token = request.query_params.get("token","") if not token: raise AuthenticationFailed("没有携带token") user_obj = UserInfo.objects.filter(token=token).first() if user_obj: return (user_obj,token) # 并把返回值赋值给request.user和request.auth raise AuthenticationFailed("token不合法") # 在模型 class UserInfo(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=32) token = models.UUIDField(null=True,blank=True) # 登录成功写入token的uuid # 在视图 class AuthView(APIView): authentication_classes = [MyAuth,] # 在视图中声明authentication_classes 表示只在该视图有效 def get(self, request): print(request.user) print(request.user.username) print(request.auth) return Response('登录后发送数据') # 在setting声明DEFAULT_AUTHENTICATION_CLASSES 表示全局范围应用认证 REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES':'rest_framework.authentication.BaseAuthentication' # 对应的认证控制类 } # 其他几种DRF框架自带的认证控制类 BasicAuthentication SessionAuthentication TokenAuthentication RemoteUserAuthentication
7,权限控制组件
# 权限控制类 from rest_framework.permissions import BasePermission class MyPermission(BasePermission): # 自定义的权限控制类中,必须要声明message属性和has_permission方法 message = "错误信息" def has_permission(self,request,view): # if request.user.type in [2,3]: # 判断用户是否有权限 return True # 有 return True return False # 没有 return False # 在视图 class PermissionView(APIView): authentication_classes = [MyAuth,] permission_classes = [Permission,] # 局部视图注册,只在当前视图中生效 # 如果权限控制类返回True,程序继续执行 # 返回False,则触发报错raise def get(self,request): # 这个接口只能vip或者vvip访问 return Response('权限测试接口') # 在源码 def check_permissions(self, request): for permission in self.get_permissions(): # 获取权限控制类的实例化对象 if not permission.has_permission(request, self): # 需要实现has_permission,否则走父类的方法抛出异常 self.permission_denied( request, message=getattr(permission, 'message', None) # 需要声明message属性,否则程序无法继续执行,报错 ) # 在setting中全局注册权限组件 REST_FRAMEWORK = { # 配置全局权限 "DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.BasePermission",] } # DRF框架中自带的权限控制类 AllowAny IsAuthenticated IsAdminUser IsAuthenticatedOrReadOnly DjangoModelPermissions 的子类: DjangoModelPermissionsOrAnonReadOnly DjangoObjectPermissions