Django Rest Framework学习
一、初识DRF组件
1.1 FBV和CBV
-
FBV:function based view
pythonfrom 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下
pythonfrom django.shortcuts import render, HttpResponse from django.views import View # 类要继承django的view模块 class LoginView(View): # 这个函数是固定写法,必须叫get def get(self): return HttpResponse("get请求。。。。") # 这个函数是固定写法,必须叫post def post(self): return HttpResponse("post请求。。。。")
2.urls.py下使用 django的view
pythonfrom 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 类、面向对象基础
运算符的应用
代码:能看懂这个就能理解操作符优先级
python
class B(object):
def __init__(self, name, age):
self.name = name
self.age = age
self.foo()
def foo(self):
print("B foo....")
class Animal(B):
foo = 100
def foo(self):
print("A foo...")
foo = 100
scd = Animal("dfdk", 23)
反射:getattr()函数
python
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
ads = Animal("hdsdf", 23)
atrr = input(">>>查看ads的哪个属性:")
print(getattr(ads, atrr))
反射二 getattr 方法执行通过实例对象(ads)去调用Animal类的属性和方法
getattract(self(实列对象),类中的函数)
python
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age
def 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)
子类不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖
python
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路由:
python/books/ get ----- > 查看所有资源逻辑 post ----- > 添加逻辑 /books/1 get ----- > 查看单个资源逻辑 delete ----- > 删除单个资源逻辑 put ----- > 更新单个资源逻辑 patch ----- > 更新资源逻辑
1.5 drf的使用
-
在setting.py 的INSTALLED_APPS下添加" rest_framework"
-
在urls路由中
pythonfrom 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 = cls view.initkwargs = initkwargs #####第三步 在View.as_view中返回view函数(dispatch()方法) def view(request, *args, **kwargs): self = cls(**initkwargs)这个self指的是APIView return self.dispatch(request, *args, **kwargs) #####第四步 调用APIView的dispatch方法,因为APIView中有自己的dispatch()方法 #####第五步 看看APIView的dispatch方法的实现 def dispatch(self, request, *args, **kwargs): self.args = args self.kwargs = kwargs ##### 构建新的request对象 request = self.initialize_request(request, *args, **kwargs) self.request = request self.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文件
pythonfrom 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.GET print(request.GET, type(request.GET)) print(request.query_params) return HttpResponse("LoginView:get请求。。。。") def post(self, request): # 查看post请求传回来的数据 使用request.data print(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对象
-
-
序列化
-
创建序列化类
pythonfrom rest_framework import serializers # 序列化类的构建 class StudentSerializer(serializers.Serializer): name = serializers.CharField() sex = serializers.BooleanField() age = serializers.IntegerField() class_num = serializers.CharField()
-
创建序列化对象
pythonclass StudentView(APIView): def get(self, request): # 查看get请求传回来的数据 request.GET print(request.GET, type(request.GET)) print(request.query_params) # 获取数据库里的所有数据,序列化对象student student = models.Student.objects.all() StudentSerializer(instance=student,) return HttpResponse("LoginView:get请求。。。。")
-
Serializer的创建好序列化对象构造方法(默认many=false)
many = True 表示序列化多个数据对象
ptyhonstudent = 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} )
-
序列化器的使用
序列化器的使用分为两个阶段
- 处理客户端请求时,使用序列化器可以完成对数据的反序列化
- 处理服务端响应时,使用序列化器可以完成对数据的序列化
-
响应器
pythonfrom rest_framework.response import Response return Response(serializer.data)
-
queryset\instance 转换为JSON/xml/yaml格式
-
-
反序列化(添加数据)
-
校验
pythondef post(self, request): # 查看post请求传回来的数据 使用request.data print(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开发
python
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)
python""" 一、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)
pythonviews.py from 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.GET print(request.GET, type(request.GET)) print(request.query_params) # 获取数据库里的所有数据,序列化对象student s = 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): @staticmethod def get_objects(id): """ :param id: 课程id :return: 返回课程这个课程id的查询数据对象 """ try: return models.Course.objects.get(id=id) except Course.DoesNotExist: return def get(self, request, id): # 查看get请求传回来的数据 request.GET # 获取数据库里的所有数据,序列化对象student obj = 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 views urlpatterns = [ path("cbv/list/", views.CourseList.as_view()), path("cbv/list/<int:id>/", views.CourseDetailList.as_view()) ]
-
通用类视图(Generic Classed Based View)
python# 通用类导包 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 = SerializerCourse urls.py from django.urls import path, re_path, include from drfapp import views urlpatterns = [ # Class Based View path("cbv/list/", views.CourseList.as_view()), path("cbv/list/<int:id>/", views.CourseDetailList.as_view()), # Generic Class Based View path("gcbv/list/", views.GCourseList.as_view()), # 注意:这里的<int:pk>要固定写成pk path("gcbv/list/<int:pk>/", views.GCourseDetailList.as_view()) ]
put和patch的区别:
put需要写全部字段并全部更新 patch 只需要写部分需要更新的字段并添加要更改的值
-
DRF的视图集viewsets
- 方法一使用原始创建路由 url
pythonfrom django.urls import path, re_path, include from drfapp import views urlpatterns = [ # Class Based View path("cbv/list/", views.CourseList.as_view()), path("cbv/list/<int:id>/", views.CourseDetailList.as_view()), # Generic Class Based View path("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.destroy path("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
pythonfrom 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权限配置 (认证是最先被执行的 然后再权限检测)
python'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
python第一步 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接口能够拿到什么级别权限的数据
python'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ],
1.9 生成API接口文档
-
方法一
python# 第一步 在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), ]
-
方法二
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快速入门")) ]
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快速入门"))
]