Django Rest Framework学习
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):
# 这个函数是固定写法,必须叫get
def get(self):
return HttpResponse("get请求。。。。")
# 这个函数是固定写法,必须叫post
def 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()
运算符的应用
代码:能看懂这个就能理解操作符优先级
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()函数
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(实列对象),类中的函数)
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)
子类不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖
class LoginView(View):
def dispatch(self, request, *args, **kwargs):
print("dispatch....")
# 在重写方法后,在引用父类方法需要使用super()
super().dispatch(request, *args, **kwargs)
前后端不分离模式:
前后端分离模式:
目前市面上大多数公司开发人员使用的接口实现规范主要有:restful(基于Http协议)、RPC(基于TCP协议)性能好一些。
RPC (Remote Procedure Call)远程过程调用【远程服务调用】,从字面上理解就是访问/调用远程服务端提供的API接口
restful :面向资源开发
符合restful 规范 urls路由:
/books/
get ----- > 查看所有资源逻辑
post ----- > 添加逻辑
/books/1
get ----- > 查看单个资源逻辑
delete ----- > 删除单个资源逻辑
put ----- > 更新单个资源逻辑
patch ----- > 更新资源逻辑
在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 = 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文件
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.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属性:
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.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 表示序列化多个数据对象
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.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)
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)
函数式编程(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.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)
# 通用类导包
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
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()),
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
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))
]
认证: 认证 是指一个用户登录时对其身份的校验、认证; 在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',
],
方法一
# 第一步
在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快速入门"))
]