Django Rest Framework学习
一、初识DRF组件
1.1 FBV和CBV
-
FBV:function based view
from django.shortcuts import render, HttpResponse # 这种是基于FBV模式 函数模式实现Views函数 def login(request):if request.method == 'GET':return HttpResponse("Get请求。。。。")return HttpResponse("POST请求。。。。")
在FBV下调函数不加(),例如path(‘login/’, views.Login)url是映射,指向这块空间
-
CBV:class based view
1.app目录下的views.py下
from django.shortcuts import render, HttpResponse from django.views import View # 类要继承django的view模块 class LoginView(View):# 这个函数是固定写法,必须叫getdef get(self):return HttpResponse("get请求。。。。")# 这个函数是固定写法,必须叫postdef post(self):return HttpResponse("post请求。。。。")
2.urls.py下使用 django的view
from django.contrib import admin from django.urls import path from userapp import views urlpatterns = [# path('admin/', admin.site.urls),path('login/', views.LoginView.as_view()), ] '''path('login/', views.LoginView.as_view()),相当于:path('login/', View.view),一旦浏览器发起get请求 /login/ ————> View.view()def view(request, *args, **kwargs):self = cls(**initkwargs)return self.dispatch(request, *args, **kwargs)然后view()去调用dispatch()方法def dispatch(self, request, *args, **kwargs):if request.method.lower() in self.http_method_names:handler = getattr(self, request.method.lower(), self.http_method_not_allowed)else:handler = self.http_method_not_allowed# 返回loginview里的get、post方法return handler(request, *args, **kwargs) '''
在CBV模式下重要的是as_view()
1.2 类、面向对象基础
运算符的应用
代码:能看懂这个就能理解操作符优先级
class B(object):def __init__(self, name, age):self.name = nameself.age = ageself.foo()def foo(self):print("B foo....")
class Animal(B):foo = 100def foo(self):print("A foo...")foo = 100
scd = Animal("dfdk", 23)
反射:getattr()函数
class Animal(object):def __init__(self, name, age):self.name = nameself.age = age
ads = Animal("hdsdf", 23)
atrr = input(">>>查看ads的哪个属性:")
print(getattr(ads, atrr))
反射二 getattr 方法执行通过实例对象(ads)去调用Animal类的属性和方法
getattract(self(实列对象),类中的函数)
class Animal(object):def __init__(self, name, age):self.name = nameself.age = agedef foo(self):print("foo zhixingl")
ads = Animal("hdsdf", 23)atrr = input(">>>查看ads的哪个属性:")
print(getattr(ads, atrr))
getattr(ads, "foo")()
1.重载(overloading method)
是在一个类里面,方法名字相同,而参数不同。返回类型呢?可以相同也可以不同。重载是让类以统一的方式处理不同类型数据的一种手段。函数重载主要是为了解决两个问题。
1.可变参数类型。
2.可变参数个数。
另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。
好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。
那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。
好了,鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。
2.方法重写(overiding method)
子类不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖
class LoginView(View):def dispatch(self, request, *args, **kwargs):print("dispatch....")# 在重写方法后,在引用父类方法需要使用super()super().dispatch(request, *args, **kwargs)
1.3 前后端分离模式
-
前后端不分离模式:
- 将数据库的数据直接嵌入模板中 实现方法是使用模板语法
- views.py里要返回页面 使用render(),HttpResponse,redirct()
-
前后端分离模式:
- 使用后端的数据,queryset ,序列化(将数据库的数据传到客户端页面)、反序列化(将客户端的数据返回并保存到数据库)
1.4 API
目前市面上大多数公司开发人员使用的接口实现规范主要有:restful(基于Http协议)、RPC(基于TCP协议)性能好一些。
-
RPC (Remote Procedure Call)远程过程调用【远程服务调用】,从字面上理解就是访问/调用远程服务端提供的API接口
-
restful :面向资源开发
符合restful 规范 urls路由:
/books/get ----- > 查看所有资源逻辑post ----- > 添加逻辑 /books/1get ----- > 查看单个资源逻辑delete ----- > 删除单个资源逻辑put ----- > 更新单个资源逻辑patch ----- > 更新资源逻辑
1.5 drf的使用
-
在setting.py 的INSTALLED_APPS下添加” rest_framework“
-
在urls路由中
from django.contrib import admin from django.urls import path from userapp.views import LoginView from drfdemo import views urlpatterns = [# path('admin/', admin.site.urls),path('login/', LoginView.as_view()),path('student/', views.StudentView.as_view()), ] 因为DRF框架的views的类视图继承的是rest_framework的APIView类 所以 path('student/', views.StudentView.as_view()),中调用的是rest_framework的方法 ''' 类视图继承的是rest_framework的APIView类def as_view(cls, **initkwargs):########第二步调用django的View类里的as_view()函数 #######view = super().as_view(**initkwargs)view.cls = clsview.initkwargs = initkwargs#####第三步 在View.as_view中返回view函数(dispatch()方法)def view(request, *args, **kwargs):self = cls(**initkwargs)这个self指的是APIViewreturn self.dispatch(request, *args, **kwargs)#####第四步 调用APIView的dispatch方法,因为APIView中有自己的dispatch()方法#####第五步 看看APIView的dispatch方法的实现def dispatch(self, request, *args, **kwargs):self.args = argsself.kwargs = kwargs##### 构建新的request对象request = self.initialize_request(request, *args, **kwargs)self.request = requestself.headers = self.default_response_headers # deprecate?##### 执行三个组件 权限、认证(验证有点像Django的modelForms)、限流self.initial(request, *args, **kwargs)##### 分发逻辑(使用getattr()反射去StudentView下找到相关的函数并执行)handler = getattr(self, request.method.lower(), self.http_method_not_allowed)handler = self.http_method_not_allowed# 返回loginview里的get、post方法return handler(request, *args, **kwargs) '''
-
views.py文件
from django.shortcuts import render, HttpResponse # from django.views import View # 序列化器和检验数据的功能 from rest_framework.views import APIView # Create your views here. class StudentView(APIView):def get(self, request):# 查看get请求传回来的数据 request.GETprint(request.GET, type(request.GET))print(request.query_params)return HttpResponse("LoginView:get请求。。。。")def post(self, request):# 查看post请求传回来的数据 使用request.dataprint(request.data, type(request.data))return HttpResponse("LoginView:post请求。。。。")def delete(self, request):return HttpResponse("LoginView:delete请求。。。。")
请求参数的反序列化,
-
request.data
返回解析之后的请求体数据,类似于Django里的request.POST和request.FILES属性:
- 包含了解析之后的文件和非文件数据
- 包含了对POST、PUT、PATCH请求方式解析后的数据
- 利用了REST Framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
-
request.query_params
与DJango标准的request.GET相同,只是更换了更正确的名称而已
-
request._request
获取django封装的Request对象
-
-
序列化
-
创建序列化类
from rest_framework import serializers # 序列化类的构建 class StudentSerializer(serializers.Serializer):name = serializers.CharField()sex = serializers.BooleanField()age = serializers.IntegerField()class_num = serializers.CharField()
-
创建序列化对象
class StudentView(APIView):def get(self, request):# 查看get请求传回来的数据 request.GETprint(request.GET, type(request.GET))print(request.query_params)# 获取数据库里的所有数据,序列化对象studentstudent = models.Student.objects.all()StudentSerializer(instance=student,)return HttpResponse("LoginView:get请求。。。。")
-
Serializer的创建好序列化对象构造方法(默认many=false)
many = True 表示序列化多个数据对象
student = models.Student.objects.all()# 可能是多个数据对象所以使用many=True StudentSerializer(instance=student, data=empty,many=True )# many =True 表示多个序列化数据
说明:
1)用序列化时,将模型类对象传入instance参数
2)用反序列化时,将要被反序列化的数据传入data参数
3)除了instance参数、data参数外,构造的Serializer对象时还可以通过context参数额外添加数据,如
StudentSerializer(instance=student, context={"request":request} )
-
序列化器的使用
序列化器的使用分为两个阶段
- 处理客户端请求时,使用序列化器可以完成对数据的反序列化
- 处理服务端响应时,使用序列化器可以完成对数据的序列化
-
响应器
from rest_framework.response import Response return Response(serializer.data)
-
queryset\instance 转换为JSON/xml/yaml格式
-
-
反序列化(添加数据)
-
校验
def post(self, request):# 查看post请求传回来的数据 使用request.dataprint(request.data, type(request.data))serializer = StudentSerializer(data=request.data)# 验证方法一try:serializer.is_valid(raise_exception=True)# 将数据库插入数据库models.Student.objects.create(**serializer.validated_data)except:return Response(serializer.errors)# 验证方法二if serializer.is_valid():models.Student.objects.create(**serializer.validated_data)return Response(serializer.errors)
-
1.6 由Django开发API View开发
from django.shortcuts import render
import json
from django.http import HttpResponse, JsonResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
# Create your views here.courser_list = {'name': '课程','price': 999.1
}# FBV
@csrf_exempt
def course_list(request):if request.method == "GET":# return HttpResponse(json.dump(courser_list), content_type='application/json')return JsonResponse(courser_list)course = json.loads(request.body.decode('utf-8'))return JsonResponse(course, safe=False)# CBV
@method_decorator(csrf_exempt, name='dispatch')
class CourseList(View):def get(self,request):return JsonResponse(courser_list)def post(self, request):course = json.loads(request.body.decode('utf-8'))return JsonResponse(course, safe=False)
1.7 由DRF 开发API view
-
函数式编程(Function Based View)
""" 一、DRF函数式编程 FBV (Function Based View)""" @api_view(["GET", "POST"]) def course_list(request):if request.method == 'GET':course = Course.objects.all()sercourse = SerializerCourse(instance=course, many=True) # 序列化return Response(data=sercourse.data, status=status.HTTP_200_OK)elif request.method == 'POST':s = SerializerCourse(data=request.data) # 反序列化if s.is_valid():s.save(teacher=request.user)return Response(data=s.data, status=status.HTTP_201_CREATED)return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)
-
类试图编程(Classed Based View)
views.pyfrom rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status # 状态码 from rest_framework.views import APIView from drfapp import models from .models import Course from .serializers import SerializerCourse"""二、类视图 CLass Based View""" class CourseList(APIView):def get(self, request):# 查看get请求传回来的数据 request.GETprint(request.GET, type(request.GET))print(request.query_params)# 获取数据库里的所有数据,序列化对象students = models.Course.objects.all()ser = SerializerCourse(instance=s, many=True)return Response(data=ser.data)def post(self,request):s = SerializerCourse(data=request.data) # 反序列化if s.is_valid():s.save()print(type(request.data),type(s.data))return Response(data=s.data, status=status.HTTP_201_CREATED)return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)class CourseDetailList(APIView):@staticmethoddef get_objects(id):""":param id: 课程id:return: 返回课程这个课程id的查询数据对象"""try:return models.Course.objects.get(id=id)except Course.DoesNotExist:returndef get(self, request, id):# 查看get请求传回来的数据 request.GET# 获取数据库里的所有数据,序列化对象studentobj = self.get_objects(id=id)# 如果没查询的结果if not obj:return Response(data={'msg': "此课程不存在"}, status=status.HTTP_404_NOT_FOUND)s = SerializerCourse(instance=obj, many=False)print(type(request.data), type(s.data))return Response(data=s.data, status=status.HTTP_200_OK)def put(self, request, id):""":param request::param id::return:"""obj = self.get_objects(id=id)# 如果没查询的结果if not obj:return Response(data={'msg': "此课程不存在"}, status=status.HTTP_404_NOT_FOUND)s = SerializerCourse(instance=obj, data=request.data)if s.is_valid():s.save()print(type(request.data), type(s.data))return Response(data=s.data, status=status.HTTP_201_CREATED)return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)def delete(self, request, id):obj = self.get_objects(id=id)# 如果没查询的结果if not obj:return Response(data={'msg': "此课程不存在"}, status=status.HTTP_404_NOT_FOUND)obj.delete()return Response(status=status.HTTP_204_NO_CONTENT)urls.py from django.urls import path, re_path, include from drfapp import viewsurlpatterns = [path("cbv/list/", views.CourseList.as_view()),path("cbv/list/<int:id>/", views.CourseDetailList.as_view()) ]
-
通用类视图(Generic Classed Based View)
# 通用类导包 from rest_framework import generics"""三、通用类视图 Generic CLass Based View""" class GCourseList(generics.ListCreateAPIView):queryset = models.Course.objects.all()serializer_class = SerializerCourse# from rest_framework import mixins # from rest_framework.generics import GenericAPIView # class GCourseDetailList((mixins.RetrieveModelMixin, # mixins.UpdateModelMixin, # mixins.DestroyModelMixin, # GenericAPIView)): class GCourseDetailList(generics.RetrieveUpdateDestroyAPIView):queryset = models.Course.objects.all()serializer_class = SerializerCourseurls.py from django.urls import path, re_path, include from drfapp import viewsurlpatterns = [# Class Based Viewpath("cbv/list/", views.CourseList.as_view()),path("cbv/list/<int:id>/", views.CourseDetailList.as_view()),# Generic Class Based Viewpath("gcbv/list/", views.GCourseList.as_view()),# 注意:这里的<int:pk>要固定写成pkpath("gcbv/list/<int:pk>/", views.GCourseDetailList.as_view()) ]
put和patch的区别:
put需要写全部字段并全部更新 patch 只需要写部分需要更新的字段并添加要更改的值
-
DRF的视图集viewsets
- 方法一使用原始创建路由 url
from django.urls import path, re_path, include from drfapp import viewsurlpatterns = [# Class Based Viewpath("cbv/list/", views.CourseList.as_view()),path("cbv/list/<int:id>/", views.CourseDetailList.as_view()),# Generic Class Based Viewpath("gcbv/list/", views.GCourseList.as_view()),path("gcbv/list/<int:pk>/", views.GCourseDetailList.as_view()),# DRF viewsets ######## 方法一:# get的值是:viewsets.ModelViewSet继承的ListModelMixin.list# post值:viewsets.ModelViewSet继承的CreateModelMixin.create# get的值是:viewsets.ModelViewSet继承的RetrieveModelMixin.retrieve# put值:viewsets.ModelViewSet继承的UpdateModelMixin.update# patch值:viewsets.ModelViewSet继承的UpdateModelMixin.partial_update# put值:viewsets.ModelViewSet继承的DestroyModelMixin.destroypath("viewsets/", views.CourseViewSet.as_view({"get": "list", "post": "create"})),path("viewsets/<int:pk>/", views.CourseViewSet.as_view({"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"})) ]
-
DRF的视图集viewsets 方法一使用原始创建路由 url
- 方法二 使用DRF的routers创建路由 url
from django.urls import path, re_path, include from drfapp import views# DRF viewsets ######## 方法二使用DRF的路由: from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register(prefix="viewsets", viewset=views.CourseViewSet)urlpatterns = [path("", include(router.urls)) ]
1.8 权限与认证
-
认证: 认证 是指一个用户登录时对其身份的校验、认证; 在setting.py进行drf权限配置 (认证是最先被执行的 然后再权限检测)
'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.BasicAuthentication','rest_framework.authentication.SessionAuthentication','rest_framework.authentication.TokenAuthentication']
-
Token验证的生成方式
-
使用Django的manage.py生成Token
生成Token的命令 python manage.py drf_create_token admin
-
使用Django的信号机制生成Token
第一步 settings.文件下INSTALLED_APPS = [ 'rest_framework.authtoken', # DRF 自带的Token认证 ] 第二步 views.py文件下 # 通过信号机制生成Token from django.db.models.signals import post_save from django.dispatch import receiver # django的user模型类 from django.conf import settings from rest_framework.authtoken.models import Token# 通过信号机制生成token @receiver(post_save, sender=settings.AUTH_USER_MODEL) #Django的信号机制 def generate_token(sender, instance=None, created=False, **kwargs):"""创建用户时自动生成token:param sender::param instance::param created::param kwargs::return:"""if created:Token.objects.create(user=instance)第三步 drf_kan项目的总路由:drf_kan/urls.py from rest_framework.authtoken import views urlpatterns = [path('api-token-auth', views.obtain_auth_token), # 获取Token的接口 ]
-
-
权限:权限是指一个登录验证通过的用户能够访问哪些接口API,或者时对某个API接口能够拿到什么级别权限的数据
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated',],
1.9 生成API接口文档
-
方法一
# 第一步 在settings.py中 # DRF的全局配置 REST_FRAMEWORK = {'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',} #第二步 在drf_kan项目的总路由:drf_kan/urls.py文件中 from rest_framework.schemas import get_schema_view # 概要 生成接口文档的包schema_view = get_schema_view(title="DRF API Document", description="xxxx") urlpatterns = [path('schema/', schema_view),]
-
方法二
# 第一步 在settings.py中 # DRF的全局配置 REST_FRAMEWORK = {# 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema','DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',} #第二步 在drf_kan项目的总路由:drf_kan/urls.py文件中 from rest_framework.documentation import include_docs_urls # 生成接口文档的包urlpatterns = [path('docs/', include_docs_urls(title="DRF API Document", description="DRF快速入门"))]
toSchema’,}
#第二步
在drf_kan项目的总路由:drf_kan/urls.py文件中
from rest_framework.schemas import get_schema_view # 概要 生成接口文档的包
schema_view = get_schema_view(title=“DRF API Document”, description=“xxxx”)
urlpatterns = [
path(‘schema/’, schema_view),
]
- 方法二```python
# 第一步
在settings.py中
# DRF的全局配置
REST_FRAMEWORK = {# 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema','DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',}
#第二步
在drf_kan项目的总路由:drf_kan/urls.py文件中
from rest_framework.documentation import include_docs_urls # 生成接口文档的包urlpatterns = [path('docs/', include_docs_urls(title="DRF API Document", description="DRF快速入门"))]