所有文章 > API开发 > Django REST framework实现API之基础篇

Django REST framework实现API之基础篇

一、RESTful API

  当前发展前端设备(手机、电脑、平板等)层出不穷,而后端不会随着前端去对应每一种进行通信,因此提出来统一接口进行调度,才有了API的概念,RESTful API是目前成熟的应用程序API设计理论。同一套API接口,能使用多次,API即所谓的接口URL

二、Restful规范


1.通信协议
    API与用户的通信协议,总是使用HTTPs协议。
2.域名
    在URL中体现API,将API部署在专有域名下,避免跨域请求
3.版本
    API的版本号放入URL。https://api.example.com/v1/
    另一种做法是,将版本号放在HTTP头信息中
4.路径
    路径又称”终点”(endpoint),表示API的具体网址。在RESTful架构中,每个网址代表一种资源(resource),API中的名词应该使用复数。
    https://api.example.com/v1/animals
    https://api.example.com/v1/employees
5.方法
    常用的HTTP动词有下面五个(括号里是对应的SQL命令)。

GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
DELETE(DELETE):从服务器删除资源。
HEAD:获取资源的元数据。
OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的

6.过滤信息(Filtering)如果记录数量很多,服务器不可能都返回给用户。API应该提供参数,过滤返回结果。
下面是一些常见的参数。

https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件

参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoo/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。
7.状态码
服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。

200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

状态码的完全列表参见:https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html。
8.错误信息
    如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。
9.返回结果(尽量使用Json格式)
    针对不同操作,服务器向用户返回的结果应该符合以下规范。

GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档

10.超链接
    即返回结果中提供链接,连向其他API方法

三、Django restframework介绍

Django Rest Framework 是一个强大且灵活的工具包,基于restful 规范开发,用以构建Web API。可以在Django的基础上迅速实现API,并且自身还带有WEB的测试页面,可以方便的测试自己的API,主要运用于前后端分离的WEB应用中。序列化使用方法和Django的form组件很类似。

四、Django restframework生命周期

发送请求–>Django的wsgi–>中间件–>路由系统_执行CBV的as_view(),就是执行内部的dispath方法–>在dispath内,对request封装–>版本–>认证–>权限–>限流–>视图–>如果视图用到缓存( request.data or request.query_params )就用到了 解析器–>视图处理数据,用到了序列化(对数据进行序列化或验证) –>视图返回数据可以用到分页


五、安装django restframework

1、安装
pip install djangorestframework
2、配置setting
如果想要获取一个图形化的页面,需要将 rest_framework 注册到项目的INSTALL_APPS中。

INSTALLED_APPS = [
...
'rest_framework',
'api',
...
]

六、Django RESTful Framework案例


1、路由urls

from django.urls import path, include
from views import TestView
app_name = 'api'
urlpatterns = [
path('test/', TestView.as_view(), name='test')
]

2、视图views

from django.http import JsonResponse
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView
class TestView(APIView):
def dispatch(self, request, *args, **kwargs):
"""
请求到来之后,都要请求dispatch方法,dispath方法根据不同的请求方式出发get/post/put等方法
注意:APIView中,dispath方法有好多好多功能
"""
return super().dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
data = {
"status": 200,
'data': 'get success'
}
return Response(data, status=201)
def post(self, request, *args, **kwargs):
data = {
'status': 200,
'data': 'post success'
}
return Response(data, status=201)

七、Djangorestframework视图分类


视图类 GenericAPIView:包含两大视图类(APIView、GenericAPIView)
视图工具类 mixins:包含五大工具类,六大工具方法
工具视图类 generics:包含九大工具视图类,一堆mixins工具类与GenericAPIView视图基类组合
视图集 viewsets:可自定义映射关系

八、视图类 GenericAPIView

1.APIView
from rest_framework.views import APIView
继承基本View,拥有View所有的功能
重写了as_view()方法,禁用了csrf认证
重写dispatch,封装请求、响应、渲染、异常、解析、三大认证模块
封装一堆属性,可完成视图类的局部配置

1、renderer_classes  渲染的类
2、parser_classes 解析转换的类
3、authentication_classes 认证的类
4、throttle_classes 节流的类,控制请求频率
5、permission_classes 权限的类
6、content_negotiation_class 内容过滤类
7、metadata_class 元信息的类
8、versioning_class 版本控制的类
9、as_view()


调用父类中的as_view ->dispatch方法
dispatch方法被重写
initialize_request 使用django的request构建了一个REST中的Request initial 

perform_authentication
  执行用户认证,遍历认证器,如果认证成功返回一个元祖,元祖中的第一个元素就是user,第二个元素就是auth,token
  check_permissions
  检查权限,遍历我们的权限检测器,只要有一个权限检测没有通过,就直接显示权限被拒绝,所有权限都满足,才算是拥有权限。
  check_throttles 
  检测频率,遍历频率限制器,如果验证不通过,就需要等待 
  csrf_exempt
  所有APIView的自雷都是csrf豁免的

2.GenericAPIView
from rest_framework.generics import GenericAPIView
继承APIView,拥有APIView所有的功能
提供get_queryset方法:配置queryset类属性,群查获取QuerySet对象
提供get_object方法:配置lookup_url_kwarg类属性,单查获取单个对象
提供get_serializer方法:配置serializer_class类属性,提供序列化类并使用自定义的序列化类序列化
总结:GenericAPIView就是在APIView基础上额外提供了三个方法和三个类属性,如果不配合视图工具类,则体现不出来优势所在
使用它的好处:视图中的增删改查逻辑其实大差不差,但操作的资源不一致(操作的资源指的是models模型类和序列化类),将资源形成配置,操作逻辑一致,就可以完成封装。
群查

from rest_framework.generics import GenericAPIView
class ViewGenericAPIView(GenericAPIView):
# 配置关联表的属性
# 如果只写models.Car.objects的话那就是manager对象,不是QuerySet对象
queryset = models.Car.objects.filter(is_delete=False).all()
# 配置使用的序列化类
serializer_class = serializer.CarModelSerializer
# 群查
def get(self,request,*args,**kwargs):
# 帮我们去表里面拿数据
car_query = self.get_queryset()
# 帮我们去序列化
car_ser = self.get_serializer(car_query,many=True)
return APIResponse(results=car_ser.data)

单查

from rest_framework.generics import GenericAPIView
class ViewGenericAPIView(GenericAPIView):
# 配置关联表的属性
# 如果只写models.Car.objects的话那就是manager对象,不是QuerySet对象
queryset = models.Car.objects.filter(is_delete=False).all()
# 配置使用的序列化类
serializer_class = serializer.CarModelSerializer
# 配置查询的条件为pk,单查走pk过滤的条件
lookup_url_kwarg = 'pk'
# 单查
def get(self,request,*args,**kwargs):
car_obj = self.get_object()
car_ser = self.get_serializer(car_obj)
return APIResponse(results=car_ser.data)

九、视图工具类 mixins

在GenericAPIView的基础上提供了五个类,六个方法六大接口(单查、群查、单增、单整体改、单局部改、单删),使用的时候需要配合继承GenericAPIView类
1.五大工具类

RetrieveModelMixin:单查类
ListModelMixin:群查类
CreateModelMixin:单增类
UpdateModelMixin:单整体和单局部改类
DestroyModelMixin:单删类

2.六大工具方法

retrieve:单查方法
list:群查方法
create:单增方法
update:单整体改方法
partial_update:单局部改方法
destroy:单删方法

3.使用mixins的六大工具方法

from rest_framework import mixins
class ViewMixinsAPIView(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializer.CarModelSerializer
lookup_url_kwarg = 'pk'
# 单查群查
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
return self.retrieve(request, *args, **kwargs)
return self.list(request, *args, **kwargs)
# 单增
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
# 单整体改
def put(self,request, *args, **kwargs):
return self.update(request, *args, **kwargs)
# 单局部改
def patch(self,request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
# 单删
def delete(self,request, *args, **kwargs):
# django中的删除是真正的删除
# 删除接口一般是自己实现重写到的,因为真正的业务不需要真正的删除
pass
# django源代码中是真的删除
return self.destroy(request, *args, **kwargs)

十、工具视图类 generics

工具类加视图类的组合,只要继承工具该类,就有响应的方法,帮我们将不同的mixins工具类与GenericAPIView视图类进行组合,我们不再需要继承GenericAPIView类
不同的组合封装成一个个的类,实现对应的请求方法(getpostputpatchdelete),随后就是用单查就继承单查的接口,用群查就继承群查的接口即可。
使用generics的工具类实现接口

from rest_framework import generics
class ViewGenericsAPIView(generics.RetrieveAPIView,
generics.ListAPIView,
generics.CreateAPIView,
generics.UpdateAPIView,
generics.DestroyAPIView):
# 单查和群查只能使用一个get,具体调用哪个要看继承的顺序
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializer.CarModelSerializer
lookup_url_kwarg = 'pk'
# 有删除需求的接口继承DestroyAPIView,重写destroy完成字段的删除
def destroy(self, request, *args, **kwargs):
pass

十一、视图集 viewsets

重写as_view方法,增加action参数(可以完成路由层的请求方法映射关系)
可以在路由层中自定义请求方法的映射关系,可自定义路由层中请求方法的映射关系来实现接口。
路由层

url(r'^v5/cars/$', views.ViewViewsetsAPIView.as_view({
"get":"list",
"post":"create"
})),
url(r'^v5/cars/(?P<pk>\d+)/$', views.ViewViewsetsAPIView.as_view({
"get":"retrieve",
"put":"update",
"patch":"partial_update",
"delete":"destroy"
})),

视图层

from rest_framework import viewsets
# 视图集类
class ViewViewsetsAPIView(viewsets.ModelViewSet):
queryset = models.Car.objects.filter(is_delete=False).all()
serializer_class = serializer.CarModelSerializer
lookup_url_kwarg = 'pk'

十二、实现群增,群整体改,群局部改,群删四个接口

 # 群整体改
def many_update(self,request,*args,**kwargs):
try:
pks = []
for dic in request.data:
pks.append(dic.pop('pk'))
car_query = models.Car.objects.filter(is_delete=False, pk__in=pks).all()
if len(pks) != len(car_query):
raise Exception('pk对象不存在')
except Exception as e:
return Response({'detail': '%s' % e}, status=400)
car_ser = self.get_serializer(instance=car_query,data=request.data,many=True)
car_ser.is_valid(raise_exception=True)
car_obj = car_ser.save()
return APIResponse(results=self.get_serializer(car_obj,many=True).data)
# 群局部改
def many_partial_update(self,request,*args,**kwargs):
try:
pks = []
for dic in request.data:
pks.append(dic.pop('pk'))
car_query = models.Car.objects.filter(is_delete=False, pk__in=pks).all()
if len(pks) != len(car_query):
raise Exception('pk对象不存在')
except Exception as e:
return Response({'detail': '%s' % e}, status=400)
car_ser = self.get_serializer(instance=car_query,data=request.data,many=True,partial=True)
car_ser.is_valid(raise_exception=True)
car_obj = car_ser.save()
return APIResponse(results=self.get_serializer(car_obj,many=True).data)
# 群删
def many_destroy(self,request,*args,**kwargs):
pks = request.data
try:
rows = models.Car.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
except:
return APIResponse(1, '数据有误')
if rows:
return APIResponse(msg='删除成功')
return APIResponse(1, '删除失败')
# 群增和单增必须使用同一个接口,都要走create方法,重写create方法,使用逻辑拆分
def create(self, request, *args, **kwargs):
if isinstance(request.data,list):
car_ser = self.get_serializer(data=request.data,many=True)
car_ser.is_valid(raise_exception=True)
car_obj = car_ser.save()
return APIResponse(msg="群增成功",results=self.get_serializer(car_obj,many=True).data)
return super().create(request, *args, **kwargs)
实现删除只做字段的修改
# 解决2 destroy方法完成对字段的修改
def destroy(self, request, *args, **kwargs):
car_obj = self.get_object()
if not car_obj:
return APIResponse(1,msg="删除失败")
car_obj.is_delete = True
car_obj.save()
return APIResponse(msg="删除成功")
实现返回信息包含数据状态码和状态信息
# 解决3 群查有状态码和状态信息,重写list方法
def list(self, request, *args, **kwargs):
response = super().list(request, *args, **kwargs)
return APIResponse(results=response.data)
# 重写retrieve方法,单查有状态码和状态信息
def retrieve(self, request, *args, **kwargs):
response = super().retrieve(request, *args, **kwargs)
return APIResponse(results=response.data)

后续我将针对Djangorestframework写一系列文章,让大家更好掌握使用,能写出实用的后端接口。希望大家多多支持!

文章转自微信公众号@Python与Django学习

#你可能也喜欢这些API文章!