DRF(Django REST framework)框架是建立在Django框架基础之上,本质上它就是Django的一个App,通过DRF能够快速设计符合RESTful规范的接口,并且它还提供了一些功能。
RESTful规范锁一套接口的协议,用于程序与程序见数据交换的一套默认规则
RESTful规范规定了以下几点:
示例:
http://www.takanashi.com/api/v1/user/?page=1
pip3 install djangorestframework
drf本质是一个app,所以如果想要在Django项目中使用它就需要在settings配置文件中进行注册
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework'
]
使用drf时推荐在视图中使用CBV处理请求
# urls.py
from app1 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/book/$', views.Book.as_view()),
url(r'^api/book/(?P\d+)/$' , views.Book.as_view()),
]
视图中根据请求不同做不同的处理,返回json格式的数据
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
# Create your views here.
from app1 import models
from django.forms.models import model_to_dict
class Book(APIView):
def get(self,request,*args,**kwargs):
# 查询
pk = kwargs.get("pk")
if pk:
book = models.Book.objects.filter(pk=pk).first()
return Response(model_to_dict(book))
books = models.Book.objects.all().values()
return Response(books)
def post(self,request,*args,**kwargs):
# 增加
book = models.Book.objects.create(**request.data)
return Response(model_to_dict(book))# model_to_dict将一个对象转换为字典
def put(self,request,*args,**kwargs):
# 修改
pk = kwargs.get("pk")
models.Book.objects.filter(id=pk).update(**request.data)
return Response("ok")
def delete(self,request,*args,**kwargs):
# 删除
pk = kwargs.get("pk")
models.Book.objects.get(pk=pk).delete()
return Response("ok")
drf中的serializers为我们提供了数据校验、序列化的功能
from rest_framework.views import APIView
from rest_framework.response import Response
from app1 import models
# 导入serializers
from rest_framework import serializers
class BookSerializers(serializers.ModelSerializer):
# 自定义字段:
# 方式一:source指定显示内容,required指定校验时忽略
title_1 = serializers.CharField(source="title",required=False)
# 方式二:这种写法需要写一个以get_开头的钩子函数,这个函数返回上面就显示什么
state_1 = serializers.SerializerMethodField()
# 如果想要返回orm中使用了choices的字段只需要这样写,不需要加括号,内部会检查是否可执行,如果可执行会自动加括号
state_2 = serializers.CharField(source="get_status_display",required=False)
class Meta:
# 指定表
model = models.Book
# 指定字段,全部字段、排除指定字段、选择指定字段
# fields = "__all__"
# exclude = ["id"]
fields = ["id","title","title_1","title_2","state_1","state_2"]
# 钩子函数,obj就是当前对象
def get_title_2(self,obj):
return obj.title
def get_state_1(self, obj):
return obj.get_status_display()
class Book(APIView):
def get(self,request,*args,**kwargs):
pk = kwargs.get("pk")
if pk:
book = models.Book.objects.filter(pk=pk).first()
# 实例化序列化对象,instance指定数据,many默认等于False,表示查询结果为一条数据
ser = BookSerializers(instance=book,many=False)
return Response(ser.data)
books = models.Book.objects.all().values()
# many=True表示查询结果有多条数据
ser = BookSerializers(instance=books,many=True)
return Response(ser.data)
def post(self,request,*args,**kwargs):
# data指定保存的数据
ser = BookSerializers(data=request.data)
# 校验数据
if ser.is_valid():
# 保存
ser.save()
# 返回保存的数据
return Response(ser.data)
# 返回错误信息
return Response(ser.errors)
def put(self,request,*args,**kwargs):
# 全部修改
pk = kwargs.get('pk')
book = models.Book.objects.filter(id=pk).first()
# instance指定修改哪条数据,data指定钥修改为的数据
ser = BookSerializers(instance=book, data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
return Response(ser.errors)
def patch(self, request, *args, **kwargs):
# 局部修改
pk = kwargs.get('pk')
book = models.Book.objects.filter(id=pk).first()
# partial=True指定此次修改为局部修改
ser = BookSerializers(instance=book,data=request.data,partial=True)
if ser.is_valid():
ser.save()
return Response(ser.data)
return Response(ser.errors)
from app1 import models
from rest_framework import serializers
from rest_framework.generics import GenericAPIView,ListAPIView
class BookSerializers(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = "__all__"
class Book(ListAPIView):
queryset = models.Book.objects.all()
# 序列化
serializer_class = BookSerializers
from app1 import models
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.generics import GenericAPIView,ListAPIView
class BookSerializers(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = "__all__"
class Book(APIView):
def get(self,request,*args,**kwargs):
pk = kwargs.get("pk")
if pk:
book = models.Book.objects.filter(pk=pk).first()
ser = BookSerializers(instance=book,many=False)
return Response(ser.data)
condition = {}
# 获取条件
title = request.query_params.get('title')
if title:
condition['title'] = title
books = models.Book.objects.filter(**condition).order_by('pk')
ser = BookSerializers(instance=books,many=True)
return Response(ser.data)
from app1 import models
from rest_framework import serializers
from rest_framework.generics import GenericAPIView,ListAPIView
# 导入BaseFilterBackend
from rest_framework.filters import BaseFilterBackend
class BookSerializers(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = "__all__"
# 自定义分页类
class MyFilterBackend(BaseFilterBackend):
# 必须实现filter_queryset方法
def filter_queryset(self, request, queryset, view):
val = request.query_params.get('title')
return queryset.filter(title=val)
class Book(ListAPIView):
queryset = models.Book.objects.all()
# 序列化
serializer_class = BookSerializers
# 分页
filter_backends = [MyFilterBackend,]
方式一:
通过PageNumberPagination类进行分页,访问URL使用以下方式:http://api.example.org/accounts/?page=4
在settings配置中写入以下配置
REST_FRAMEWORK = {
# 默认显示数
"PAGE_SIZE":2,
}
from rest_framework.views import APIView
from rest_framework.response import Response
from app1 import models
from rest_framework import serializers
# 导入PageNumberPagination
from rest_framework.pagination import PageNumberPagination
class BookSerializers(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = "__all__"
class Book(APIView):
def get(self,request,*args,**kwargs):
queryset = models.Book.objects.all()
# 实例化分页类
page_obj = PageNumberPagination()
# 调用paginate_queryset方法对数据进行分页
result = page_obj.paginate_queryset(queryset,request,self)
print(result) # 结果为列表
# 对分页结果进行序列化
ser = BookSerializers(instance=result,many=True)
# 只返回数据
return Response(ser.data)
# 返回包含数据、总数据个数、上一页url、下一页url,也可以通过重写get_paginated_response方法自定义显示内容
return page_obj.get_paginated_response(ser.data)
方式二:
通过LimitOffsetPagination类进行分页,访问URL使用以下方式:http://127.0.0.1:8000/api/book/?limit=2&offset=2,limit指定显示几条数据,offset指定从第几条数据开始
from rest_framework.views import APIView
from rest_framework.response import Response
from app1 import models
from rest_framework import serializers
# 导入LimitOffsetPagination
from rest_framework.pagination import LimitOffsetPagination
class BookSerializers(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = "__all__"
class Book(APIView):
def get(self,request,*args,**kwargs):
queryset = models.Book.objects.all()
page_obj = LimitOffsetPagination()
result = page_obj.paginate_queryset(queryset,request,self)
ser = BookSerializers(instance=result,many=True)
return Response(ser.data)
在settings配置中写入以下配置
REST_FRAMEWORK = {
# 默认显示数
"PAGE_SIZE":2,
# 指定分页类
"DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination"
}
from app1 import models
from rest_framework import serializers
from rest_framework.generics import GenericAPIView,ListAPIView
class BookSerializers(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = "__all__"
class Book(ListAPIView):
queryset = models.Book.objects.all()
# 序列化
serializer_class = BookSerializers
urls.py
urlpatterns = [
url(r'^api/(?P\w+)/book/$' , views.Book.as_view()),
]
views.py
from rest_framework.views import APIView
from rest_framework.response import Response
# 导入
from rest_framework.versioning import URLPathVersioning
class Book(APIView):
versioning_class = URLPathVersioning
def get(self,reqeust,*args,**kwargs):
print(reqeust.version)
return Response("ok")
想要在所有视图中都应用版本,可以在settings中进行配置
REST_FRAMEWORK = {
# 配置版本
"DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
# 配置允许版本
"ALLOWED_VERSIONS": ['v1', 'v2'],
# 配置url传参时的名称,默认为version
'VERSION_PARAM': 'version'
}
from app1 import models
from rest_framework.views import APIView
from rest_framework.response import Response
# 导入BaseAuthentication
from rest_framework.authentication import BaseAuthentication
class TokenAuthentication(BaseAuthentication):
# 必须实现authenticate
def authenticate(self, request):
# 获取url中的token
token = request.query_params.get("token")
book_object = models.Book.objects.filter(token=token).first()
if book_object:
return (book_object,token) # 认证成功,不再进行认证
return None # 认证成功,执行下一个认证类
return Exception() # 认证失败,返回给用户
class Book(APIView):
authentication_classes = [TokenAuthentication]
def get(self,reqeust,*args,**kwargs):
print(reqeust.user) # book_object对象
print(reqeust.auth) # token
return Response("ok")
settings中配置后所有视图都进行认证
REST_FRAMEWORK = {
# 自定义Authentication的路径
'DEFAULT_AUTHENTICATION_CLASSES' : ["authon.auth.TokenAuthentication",]
}
根据认证组件的结果判断是否有权限访问
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import BasePermission
from rest_framework import exceptions
class MyPermission(BasePermission):
def has_permission(self, request, view):
if request.user:
# 返回True表示通过,进行下一个权限认证类,
return True
# 返回False表示认证未通过由源码内抛出异常
return False
# 也可以直接手动抛出异常
# return exceptions.PermissionDenied({"code":"1001","error":"错误信息"})
# RetrieveAPIView触发
def has_object_permission(self, request, view, obj):
return True
class Book(APIView):
permission_classes = [MyPermission,]
def get(self,reqeust,*args,**kwargs):
return Response("ok")
REST_FRAMEWORK = {
# 自定义Permission的路径
'DEFAULT_PERMISSION_CLASSES':["authon.auth.MyPermission"]
}
使用全局配置后如果对某一视图不想应用该功能可以通过指定空列表的方式使其不生效
class Book(APIView):
permission_classes = []
def get(self,reqeust,*args,**kwargs):
return Response("ok")
def的频率限制本质是在内部维护了一个字典,字典以throttle_anon_用户ip做key值是一个列表,当用户请求到来时获取当前时间,然后用当前时间减去60得到一个时间戳,将列表中小于这个时间戳的记录删除,然后获取列表中元素的个数,判断是否可以进行访问,如果可以进行访问则将当前时间插入到列表第一位。
在settings中配置
REST_FRAMEWORK = {
# 配置访问次数限制 3/m 每分钟3次
'DEFAULT_THROTTLE_RATES':{"anon":'3/m'},
}
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.throttling import AnonRateThrottle,BaseThrottle
class Book(APIView):
throttle_classes = [AnonRateThrottle, ]
def get(self,reqeust,*args,**kwargs):
return Response("ok")
在settings中配置
REST_FRAMEWORK = {
# 配置访问次数限制 3/m 每分钟3次
'DEFAULT_THROTTLE_RATES':{"anon":'3/m'},
# 配置AnonRateThrottle路径
'DEFAULT_THROTTLE_CLASSES':["rest_framework.throttling.AnonRateThrottle"]
}
from rest_framework.generics import ListAPIView,CreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView
from rest_framework import serializers
from app1 import models
class BookSerializers(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = "__all__"
class Book(ListAPIView,CreateAPIView):
# 列表数据查询、增加数据
queryset = models.Book.objects.all()
serializer_class = BookSerializers
class BookDetailView(RetrieveAPIView,UpdateAPIView,DestroyAPIView):
# 单条数据查询、修改数据、删除数据
queryset = models.Book.objects.all()
serializer_class = BookSerializers
预览链接
1.启动项目:
views.Book.as_view()
该条代码会执行APIView中的as_view
方法,这个方法内部首先会执行父类(View
)的as_view
方法,父类方法返回一个view函数,当其请求到来时执行这个函数。
2.请求到来:
请求到来首先执行之前得到的view函数,在函数内部调用了APIView函数的dispath
函数,在这个函数内部执行initialze_request
函数对request进行封装:
_reqeust
,authentication_classes
并将认证列表封装。执行完这些后接着返回dispath
函数执行的initial
,在这个函数中首先会做版本的处理:
determine_version
读取视图中的版本类versioning_class
versioning_class
指定的类的determine_version
函数进行版本处理:
is_allowed_version
函数校验版本号合法性版本处理结束后会进行认证:
perform_authentication
执行reqeust.user
user
会调用_authenticator
函数进行认证
authenticator
函数authenticator
函数中做认证处理authenticator
函数返回的第一个值封装到self.user
中,将第二个值封装到self.auth
中认证处理结束后进行权限处理:
check_throttles
函数permission_classes
has_permissions
函数进行权限认证权限处理结束后进行频率限制:
check_throttles
函数throttles_classes
allow_request
进行频率限制:
3.执行视图函数:
当上面所有步骤都结束后在dispath
函数中会通过反射执行视图中对应请求的函数,“以ListAPIView的get请求举例”:
get
函数,函数内调用ListModeMixin的list
函数queryset
filter_quertset
对queryset进行筛选:
filter_backends
,得到筛选类列表filter_queryset
进行筛选paginate_queryset
对queryset进行分页:
paginate_queryset
函数quertset
进行分页4.将结果封装到response
中进行返回