Django Rest Framework 介绍
一、什么是RESTful
- REST与技术无关,代表的是一种软件的架构风格,
- REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
- 对于REST这种面向资源的构架风格,有人还提出一种全新的结构理念,即:面向资源架构
二、RESTful API设计的10种规范
- 这里我们先提到一个名词:接口
- 而接口本质上是一个url,而有时候在Java/C# 中是一种约束
- 约束继承(实现)了他的类中必须含有接口中的方法
- method(根据method不同,进行不同操作)
- GET:从服务器中取出资源
- POST:从服务器上新建一个资源
- PUT:从服务器上更新资源(客户端提供改变后的完整资源)
- PATCH:从服务器上更新资源(客户端提供改变的资源)
- DELECT:从服务器删除资源
- 面向资源编程(url的后缀是一个名词,我们可以对这个名词进行增删改查)
- http://www.xxxx.com/salary
- 体现版本
- http://www.luffycity.com/v1/salary
- http://www.luffycity.com/v2/salary
- 体现api
- http://www.luffycity.com/api/v1/salary
- http://www.luffycity.com/api/v2/salary
- https(建议使用https--安全)
- 状态码
-
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.xxxx.com/api/v2/salary?page=1&size=10
- 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范
-
GET /collection:返回资源对象的列表(数组)GET /collection/resource:返回单个资源对象POST /collection:返回新生成的资源对象PUT /collection/resource:返回完整的资源对象PATCH /collection/resource:返回完整的资源对象DELETE /collection/resource:返回一个空文档
-
- 错误处理,状态码是4xx时,应返回错误信息,error当做key
- Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么
-
ret = { code: 1000, data:{ id:1, name:'小强', depart_id:http://www.luffycity.com/api/v1/depart/8/ } }
-
Django rest Framework是干什么的呢?
- 基于这个组件可以帮助我们开发,符合RESTful接口的,提供了一些功能
- 实现什么功能了呢?
- 请求进来--->经过路由到视图,然后CBV可以继承好多类----->在rest framework源码中应该先走的是dispath而在dispath之前应该有版本控制-------->然后走的是 权限 ,认证 ,频率------> 去解析器中拿数据-------->然后验证数据=序列化,------> 呈现数据=可能用到 分页 ----->选择器
- 总结就是10种(上面绿色的10中)
创建一个快速实例
创建一个实例化的类
简单的使用
models部分:
from django.db import models# Create your models here.class Book(models.Model): title=models.CharField(max_length=32) price=models.IntegerField() pub_date=models.DateField() publish=models.ForeignKey("Publish") authors=models.ManyToManyField("Author") def __str__(self): return self.titleclass Publish(models.Model): name=models.CharField(max_length=32) email=models.EmailField() def __str__(self): return self.nameclass Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() def __str__(self): return self.name
views部分
from rest_framework.response import Responsefrom .models import *from django.shortcuts import HttpResponsefrom django.core import serializersfrom rest_framework import serializersfrom rest_framework.views import APIViewfrom rest_framework.response import Responsefrom .models import *from django.shortcuts import HttpResponsefrom django.core import serializersfrom rest_framework import serializersclass BookSerializers(serializers.Serializer): title=serializers.CharField(max_length=32) price=serializers.IntegerField() pub_date=serializers.DateField() publish=serializers.CharField(source="publish.name") #authors=serializers.CharField(source="authors.all") authors=serializers.SerializerMethodField() def get_authors(self,obj): temp=[] for author in obj.authors.all(): temp.append(author.name) return tempclass BookViewSet(APIView): def get(self,request,*args,**kwargs): book_list=Book.objects.all() # 序列化方式1: # from django.forms.models import model_to_dict # import json # data=[] # for obj in book_list: # data.append(model_to_dict(obj)) # print(data) # return HttpResponse("ok") # 序列化方式2: # data=serializers.serialize("json",book_list) # return HttpResponse(data) # 序列化方式3: bs=BookSerializers(book_list,many=True) return Response(bs.data)
在app下新建一个类 创建一个ModelSerializer类来帮助序列化
class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" depth=1
再次提交post请求时候
def get(self,request): # 取数据 # print("request.data", request.data) # print("request.data type", type(request.data)) # print(request._request.GET) # print(request.GET) # 序列化 # 方式1: # publish_list=list(Publish.objects.all().values("name","email")) # 方式2: # from django.forms.models import model_to_dict # publish_list=Publish.objects.all() # temp=[] # for obj in publish_list: # temp.append(model_to_dict(obj)) # 方式3: # from django.core import serializers # ret=serializers.serialize("json",publish_list) # 序列组件 publish_list = Publish.objects.all() ps = PublishModelSerializers(publish_list, many=True) return Response(ps.data)def post(self , request, *arg,**kwargs) bs = BookSerializers(data=request.data,many=False) # 单条数据默认是many = Fales #多条数据修改成many= True if bs.is_valid(): # print(bs.validated_data) bs.save() return Response(bs.data) else: return HttpResponse(bs.errors)
重写save中的create方法
class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" # exclude = ['authors',] # depth=1 def create(self, validated_data): authors = validated_data.pop('authors') obj = Book.objects.create(**validated_data) obj.authors.add(*authors) return obj
单条数据的get和put方法
class BookDetailViewSet(APIView): def get(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj) return Response(bs.data) def put(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,data=request.data) if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors)
超链接API:Hyperlinked
class BookSerializers(serializers.ModelSerializer): publish= serializers.HyperlinkedIdentityField( view_name='publish_detail', lookup_field="publish_id", lookup_url_kwarg="pk") class Meta: model=Book fields="__all__" #depth=1
url部分的修改
urlpatterns = [ url(r'^books/$', views.BookViewSet.as_view(),name="book_list"), url(r'^books/(?P\d+)$', views.BookDetailViewSet.as_view(),name="book_detail"), url(r'^publishers/$', views.PublishViewSet.as_view(),name="publish_list"), url(r'^publishers/(?P \d+)$', views.PublishDetailViewSet.as_view(),name="publish_detail"),]
如果报错 :
解决方法:
视图创建的三种方式
mixings
视图部分
from rest_framework.views import APIViewfrom rest_framework.response import Responsefrom .models import *from django.shortcuts import HttpResponsefrom django.core import serializersfrom rest_framework import serializersclass BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" #depth=1class PublshSerializers(serializers.ModelSerializer): class Meta: model=Publish fields="__all__" depth=1class BookViewSet(APIView): def get(self,request,*args,**kwargs): book_list=Book.objects.all() bs=BookSerializers(book_list,many=True,context={ 'request': request}) return Response(bs.data) def post(self,request,*args,**kwargs): print(request.data) bs=BookSerializers(data=request.data,many=False) if bs.is_valid(): print(bs.validated_data) bs.save() return Response(bs.data) else: return HttpResponse(bs.errors)class BookDetailViewSet(APIView): def get(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,context={ 'request': request}) return Response(bs.data) def put(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,data=request.data,context={ 'request': request}) if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors)class PublishViewSet(APIView): def get(self,request,*args,**kwargs): publish_list=Publish.objects.all() bs=PublshSerializers(publish_list,many=True,context={ 'request': request}) return Response(bs.data) def post(self,request,*args,**kwargs): bs=PublshSerializers(data=request.data,many=False) if bs.is_valid(): # print(bs.validated_data) bs.save() return Response(bs.data) else: return HttpResponse(bs.errors)class PublishDetailViewSet(APIView): def get(self,request,pk): publish_obj=Publish.objects.filter(pk=pk).first() bs=PublshSerializers(publish_obj,context={ 'request': request}) return Response(bs.data) def put(self,request,pk): publish_obj=Publish.objects.filter(pk=pk).first() bs=PublshSerializers(publish_obj,data=request.data,context={ 'request': request}) if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors)
mixin类编写视图
from rest_framework import mixinsfrom rest_framework import genericsclass BookViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializers def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs)class BookDetailViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializers def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs)
使用通用的的基于类的视图
通过使用mixin类,我们使用更少的代码重写了这些视图,但我们还可以再进一步。REST框架提供了一组已经混合好(mixed-in)的通用视图,我们可以使用它来简化我们的views.py
模块。
from rest_framework import mixinsfrom rest_framework import genericsclass BookViewSet(generics.ListCreateAPIView): queryset = Book.objects.all() serializer_class = BookSerializersclass BookDetailViewSet(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookSerializersclass PublishViewSet(generics.ListCreateAPIView): queryset = Publish.objects.all() serializer_class = PublshSerializersclass PublishDetailViewSet(generics.RetrieveUpdateDestroyAPIView): queryset = Publish.objects.all() serializer_class = PublshSerializers
使用viewsets.ModelViewSet
urls.py
url(r'^books/$',views.BookViewSet.as_view({ "get":"list","post":"create"}),name="book_list"),url(r'^books/(?P\d+)$', views.BookViewSet.as_view({ 'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy' }),name="book_detail"),
views.py
class BookViewSet(viewsets.ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializers
认证与权限组件
认证组件
局部视图的认证
在APP下的serviece/auth.py
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication from .models import *
class Authentication(BaseAuthentication): def authenticate(self,request): token=request._request.GET.get("token") token_obj=UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed("验证失败!") return (token_obj.user,token_obj)
在views.py中
def get_random_str(user): import hashlib,time ctime=str(time.time()) md5=hashlib.md5(bytes(user,encoding="utf8")) md5.update(bytes(ctime,encoding="utf8")) return md5.hexdigest()from app01.service.auth import *from django.http import JsonResponseclass LoginViewSet(APIView): authentication_classes = [Authentication,] def post(self,request,*args,**kwargs): res={ "code":1000,"msg":None} try: user=request._request.POST.get("user") pwd=request._request.POST.get("pwd") user_obj=UserInfo.objects.filter(user=user,pwd=pwd).first() print(user,pwd,user_obj) if not user_obj: res["code"]=1001 res["msg"]="用户名或者密码错误" else: token=get_random_str(user) UserToken.objects.update_or_create(user=user_obj,defaults={ "token":token}) res["token"]=token except Exception as e: res["code"]=1002 res["msg"]=e return JsonResponse(res,json_dumps_params={ "ensure_ascii":False})
全局的配置
settings.py配置如下:
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",]}
权限的组件
局部视图权限
在App.service.permissions.py
from rest_framework.permissions import BasePermissionclass SVIPPermission(BasePermission): message="SVIP才能访问!" def has_permission(self, request, view): if request.user.user_type==3: return True return False
在views.py:
from app01.service.permissions import *class BookViewSet(generics.ListCreateAPIView): permission_classes = [SVIPPermission,] queryset = Book.objects.all() serializer_class = BookSerializers
全局的视图权限
settings.py配置如下:
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",]}
访问频率(throttle)组件
局部视图throttle
在app01.service.throttles.py中:
from rest_framework.throttling import BaseThrottleVISIT_RECORD={}class VisitThrottle(BaseThrottle): def __init__(self): self.history=None def allow_request(self,request,view): remote_addr = request.META.get('REMOTE_ADDR') print(remote_addr) import time ctime=time.time() if remote_addr not in VISIT_RECORD: VISIT_RECORD[remote_addr]=[ctime,] return True history=VISIT_RECORD.get(remote_addr) self.history=history while history and history[-1]
在views.py中:
from app01.service.throttles import *class BookViewSet(generics.ListCreateAPIView): throttle_classes = [VisitThrottle,] queryset = Book.objects.all() serializer_class = BookSerializers
全局视图的throttle
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",], "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",]}
内置的throttle类
在app01.service.throttles.py修改为:
class VisitThrottle(SimpleRateThrottle): scope="visit_rate" def get_cache_key(self, request, view): return self.get_ident(request)
settings.py设置:
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",], "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",], "DEFAULT_THROTTLE_RATES":{ "visit_rate":"5/m", }}
分页
简单分页
在views.py中
from rest_framework.pagination import PageNumberPagination,LimitOffsetPaginationclass PNPagination(PageNumberPagination): page_size = 1 page_query_param = 'page' page_size_query_param = "size" max_page_size = 5class BookViewSet(viewsets.ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializers def list(self,request,*args,**kwargs): book_list=Book.objects.all() pp=LimitOffsetPagination() pager_books=pp.paginate_queryset(queryset=book_list,request=request,view=self) print(pager_books) bs=BookSerializers(pager_books,many=True) #return Response(bs.data) return pp.get_paginated_response(bs.data)
偏离分页
from rest_framework.pagination import LimitOffsetPagination
解析器
渲染器
版本