restful规范
1.根据method不同,进行不同操作
2.面向资源编程
3.体现版本
4.体现是API
5.最好用https
6.响应式设置状态码
7.条件 ?id=1
8.返回值
9.返回错误信息
10.Hypermedia API
REST framework框架
1.路由
2.视图
3.权限
4.认证
5.访问频率限制
6.序列化
7.分页
8.解析器
9.渲染器
10.版本
我对 django rest framework框架的认识!
- 路由,
- 可以通过as_view传参数,根据请求方式不同执行相应的方法
- 可以在url中设置一个结尾,类似于: .json
- 视图,
- 帮助开发者提供了一些类,并在类中提供了多个方法以供我们使用。
- 版本,
- 在url中设置version参数,用户请求时候传入参数。在request.version中获取版本,根据版本不同做不同处理
- 认证,
- 写一个类并注册到认证类,在类的的authticate方法中编写认证逻辑。
- 认证成功(user,auth)
- raise AuthticateFaild(....)
- None
- 权限
- 写一个类并注册到权限类,在类的的has_permission方法中编写认证逻辑。
- True
- False
- 频率限制
- 写一个类并注册到频率类,在类的的 allow_request/wait 方法中编写认证逻辑。
allow_request
- True
- False 如果返回False,那么就要执行wait
- 解析器,
- 根据ContentType请求头,选择不同解析器对 请求体中的数据进行解析。
POST /index/ http1.1.
host:11.11.11.11
Content-Type:url-formendo....
user=alex&age=123
POST /index/ http1.1.
host:11.11.11.11
Content-Type:application/json
{....}
- 分页
- 对从数据库中获取到的数据进行分页处理: SQL -> limit offset
- 根据页码:http://www.luffycity.com/api/v1/student/?page=1&size=10
- 根据索引:http://www.luffycity.com/api/v1/student/?offset=60&limit=10
- 根据加密:http://www.luffycity.com/api/v1/student/?page=erd8
页码越大速度越慢,为什么以及如何解决?
原因:页码越大向后需要扫描的行数越多,因为每次都是从0开始扫描。
解决:
- 限制显示的页数
- 记录当前页数据ID最大值和最小值,再次分页时,根据ID现行筛选,然后再分页。
- 序列化
- 对queryset序列化以及对请求数据格式校验。
- 渲染器
- 根据URL中传入的后缀,决定在数据如何渲染到到页面上。
视图三部曲
使用混合(mixins)
1 from rest_framework.views import APIView 2 from rest_framework.response import Response 3 from .models import * 4 from django.shortcuts import HttpResponse 5 from django.core import serializers 6 7 8 from rest_framework import serializers 9 10 11 class BookSerializers(serializers.ModelSerializer): 12 class Meta: 13 model=Book 14 fields="__all__" 15 #depth=1 16 17 18 class PublshSerializers(serializers.ModelSerializer): 19 20 class Meta: 21 model=Publish 22 fields="__all__" 23 depth=1 24 25 26 class BookViewSet(APIView): 27 28 def get(self,request,*args,**kwargs): 29 book_list=Book.objects.all() 30 bs=BookSerializers(book_list,many=True,context={'request': request}) 31 return Response(bs.data) 32 33 34 def post(self,request,*args,**kwargs): 35 print(request.data) 36 bs=BookSerializers(data=request.data,many=False) 37 if bs.is_valid(): 38 print(bs.validated_data) 39 bs.save() 40 return Response(bs.data) 41 else: 42 return HttpResponse(bs.errors) 43 44 45 class BookDetailViewSet(APIView): 46 47 def get(self,request,pk): 48 book_obj=Book.objects.filter(pk=pk).first() 49 bs=BookSerializers(book_obj,context={'request': request}) 50 return Response(bs.data) 51 52 def put(self,request,pk): 53 book_obj=Book.objects.filter(pk=pk).first() 54 bs=BookSerializers(book_obj,data=request.data,context={'request': request}) 55 if bs.is_valid(): 56 bs.save() 57 return Response(bs.data) 58 else: 59 return HttpResponse(bs.errors) 60 61 62 class PublishViewSet(APIView): 63 64 def get(self,request,*args,**kwargs): 65 publish_list=Publish.objects.all() 66 bs=PublshSerializers(publish_list,many=True,context={'request': request}) 67 return Response(bs.data) 68 69 70 def post(self,request,*args,**kwargs): 71 72 bs=PublshSerializers(data=request.data,many=False) 73 if bs.is_valid(): 74 # print(bs.validated_data) 75 bs.save() 76 return Response(bs.data) 77 else: 78 return HttpResponse(bs.errors) 79 80 81 class PublishDetailViewSet(APIView): 82 83 def get(self,request,pk): 84 85 publish_obj=Publish.objects.filter(pk=pk).first() 86 bs=PublshSerializers(publish_obj,context={'request': request}) 87 return Response(bs.data) 88 89 def put(self,request,pk): 90 publish_obj=Publish.objects.filter(pk=pk).first() 91 bs=PublshSerializers(publish_obj,data=request.data,context={'request': request}) 92 if bs.is_valid(): 93 bs.save() 94 return Response(bs.data) 95 else: 96 return HttpResponse(bs.errors)
mixin类编写视图
1 from rest_framework import mixins 2 from rest_framework import generics 3 4 class BookViewSet(mixins.ListModelMixin, 5 mixins.CreateModelMixin, 6 generics.GenericAPIView): 7 8 queryset = Book.objects.all() 9 serializer_class = BookSerializers 10 11 def get(self, request, *args, **kwargs): 12 return self.list(request, *args, **kwargs) 13 14 def post(self, request, *args, **kwargs): 15 return self.create(request, *args, **kwargs) 16 17 18 19 class BookDetailViewSet(mixins.RetrieveModelMixin, 20 mixins.UpdateModelMixin, 21 mixins.DestroyModelMixin, 22 generics.GenericAPIView): 23 queryset = Book.objects.all() 24 serializer_class = BookSerializers 25 26 def get(self, request, *args, **kwargs): 27 return self.retrieve(request, *args, **kwargs) 28 29 def put(self, request, *args, **kwargs): 30 return self.update(request, *args, **kwargs) 31 32 def delete(self, request, *args, **kwargs): 33 return self.destroy(request, *args, **kwargs)
使用通用的基于类的视图
通过使用mixin类,我们使用更少的代码重写了这些视图,但我们还可以再进一步。REST框架提供了一组已经混合好(mixed-in)的通用视图,我们可以使用它来简化我们的views.py
模块。
1 from rest_framework import mixins 2 from rest_framework import generics 3 4 class BookViewSet(generics.ListCreateAPIView): 5 6 queryset = Book.objects.all() 7 serializer_class = BookSerializers 8 9 class BookDetailViewSet(generics.RetrieveUpdateDestroyAPIView): 10 queryset = Book.objects.all() 11 serializer_class = BookSerializers 12 13 class PublishViewSet(generics.ListCreateAPIView): 14 15 queryset = Publish.objects.all() 16 serializer_class = PublshSerializers 17 18 class PublishDetailViewSet(generics.RetrieveUpdateDestroyAPIView): 19 queryset = Publish.objects.all() 20 serializer_class = PublshSerializers
使用viewsets.ModelViewSet
urls.py:
1 url(r'^books/$', views.BookViewSet.as_view({"get":"list","post":"create"}),name="book_list"), 2 url(r'^books/(?P<pk>d+)$', views.BookViewSet.as_view({ 3 'get': 'retrieve', 4 'put': 'update', 5 'patch': 'partial_update', 6 'delete': 'destroy' 7 }),name="book_detail"),
views.py:
1 class BookViewSet(viewsets.ModelViewSet): 2 queryset = Book.objects.all() 3 serializer_class = BookSerializers
认证与权限组件
认证组件
局部视图认证
在app01.service.auth.py:
通过用户的token值做认证
1 class Authentication(BaseAuthentication): 2 3 def authenticate(self,request): 4 token=request._request.GET.get("token") 5 token_obj=UserToken.objects.filter(token=token).first() 6 if not token_obj: 7 raise exceptions.AuthenticationFailed("验证失败!") 8 return (token_obj.user,token_obj)
在views.py:
1 def get_random_str(user): 2 import hashlib,time 3 ctime=str(time.time()) 4 5 md5=hashlib.md5(bytes(user,encoding="utf8")) 6 md5.update(bytes(ctime,encoding="utf8")) 7 8 return md5.hexdigest() 9 10 11 from app01.service.auth import * 12 13 from django.http import JsonResponse 14 class LoginViewSet(APIView): 15 authentication_classes = [Authentication,] 16 def post(self,request,*args,**kwargs): 17 res={"code":1000,"msg":None} 18 try: 19 user=request._request.POST.get("user") 20 pwd=request._request.POST.get("pwd") 21 user_obj=UserInfo.objects.filter(user=user,pwd=pwd).first() 22 print(user,pwd,user_obj) 23 if not user_obj: 24 res["code"]=1001 25 res["msg"]="用户名或者密码错误" 26 else: 27 token=get_random_str(user) 28 UserToken.objects.update_or_create(user=user_obj,defaults={"token":token}) 29 res["token"]=token 30 31 except Exception as e: 32 res["code"]=1002 33 res["msg"]=e 34 35 return JsonResponse(res,json_dumps_params={"ensure_ascii":False})
全局视图认证组件
settings.py配置如下:
1 REST_FRAMEWORK={ 2 "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",] 3 }
权限组件
局部视图权限
在app01.service.permissions.py中:
1 from rest_framework.permissions import BasePermission 2 class SVIPPermission(BasePermission): 3 message="SVIP才能访问!" 4 def has_permission(self, request, view): 5 if request.user.user_type==3: 6 return True 7 return False
在views.py:
1 from app01.service.permissions import * 2 3 class BookViewSet(generics.ListCreateAPIView): 4 permission_classes = [SVIPPermission,] 5 queryset = Book.objects.all() 6 serializer_class = BookSerializers
全局视图权限
settings.py配置如下:
1 REST_FRAMEWORK={ 2 "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], 3 "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",] 4 }
throttle(访问频率)组件
局部视图throttle
在app01.service.throttles.py中:
1 from rest_framework.throttling import BaseThrottle 2 3 VISIT_RECORD={} 4 class VisitThrottle(BaseThrottle): 5 6 def __init__(self): 7 self.history=None 8 9 def allow_request(self,request,view): 10 remote_addr = request.META.get('REMOTE_ADDR') 11 print(remote_addr) 12 import time 13 ctime=time.time() 14 15 if remote_addr not in VISIT_RECORD: 16 VISIT_RECORD[remote_addr]=[ctime,] 17 return True 18 19 history=VISIT_RECORD.get(remote_addr) 20 self.history=history 21 22 while history and history[-1]<ctime-60: 23 history.pop() 24 25 if len(history)<3: 26 history.insert(0,ctime) 27 return True 28 else: 29 return False 30 31 def wait(self): 32 import time 33 ctime=time.time() 34 return 60-(ctime-self.history[-1])
在views.py中:
1 from app01.service.throttles import * 2 3 class BookViewSet(generics.ListCreateAPIView): 4 throttle_classes = [VisitThrottle,] 5 queryset = Book.objects.all() 6 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修改为:
1 class VisitThrottle(SimpleRateThrottle): 2 3 scope="visit_rate" 4 def get_cache_key(self, request, view): 5 6 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", } }
解析器
request类
django的request类和rest-framework的request类的源码解析
局部视图
from rest_framework.parsers import JSONParser,FormParser class PublishViewSet(generics.ListCreateAPIView): parser_classes = [FormParser,JSONParser] queryset = Publish.objects.all() serializer_class = PublshSerializers def post(self, request, *args, **kwargs): print("request.data",request.data) return self.create(request, *args, **kwargs)
全局视图
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", }, "DEFAULT_PARSER_CLASSES":['rest_framework.parsers.FormParser',] }
序列化
ser = ServerSerializer(instance=queryset, many=True) #instance接受queryset对象或者单个model对象,当有多条数据时候,使用many=True,单个对象many=False
看序列化代码
from django.shortcuts import render,HttpResponse,redirect from django.views import View from app01 import models from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers class BookSerializers(serializers.Serializer): #我们先序列化写两个字段的数据,别忘了这里面的字段和model表中的字段变量名要一样 title = serializers.CharField(max_length=32) price = serializers.DecimalField(max_digits=5, decimal_places=2) #一对多的处理 # publish = serializers.CharField(max_length=32) #返回对象 publish_email = serializers.CharField(max_length=32, source='publish.email') # source指定返回的多对一的那个publish对象的email数据,并且我们现在找到书籍的email,所以前面的字段名称就可以不和你的publish对应好了,随便取名字 publish_name = serializers.CharField(max_length=32, source='publish.name') # source指定返回的多对一的那个publish对象的其他字段数据,可以接着写字段,也就是说关联的所有的字段的数据都可以写在这里进行序列化 #对多对的处理 # authors = serializers.CharField(max_length=32) #bookobj.authors拿到的类似于一个models.Authors.object,打印的时候这是个None # authors = serializers.CharField(max_length=32,source="authors.all") #这样写返回的是queryset类型的数据,这样给前端肯定是不行的,所以按照下面的方法写 authors = serializers.SerializerMethodField() #序列化方法字段,专门给多对多字段用的,然后下面定义一个方法,方法名称写法是这样的get_字段名,名字必须是这样 def get_authors(self,obj): #参数写一个obj,这个obj是一个一个的书籍对象,然后我们通过书籍对象来返回对应的数据 # author_list_values = obj.authors.all().values() #返回这样类型的数据也行,那么具体你要返回什么结构的数据,需要和前端人员沟通清楚,然后这里对数据进行加工 #假如加工成的数据是这种类型的[ {},{} ],就可以按照下面的逻辑来写,我简单写的,肯定有更好的逻辑来加工这些数据 author_list_values = [] author_dict = {} author_list = obj.authors.all() for i in author_list: author_dict['name'] = i.name author_list_values.append(author_dict) return author_list_values class BookView(APIView): def get(self,request): book_obj_list = models.Book.objects.all() s_books = BookSerializers(book_obj_list,many=True) return Response(s_books.data) def post(self,request): pass
serializer在内部就做了这点事儿,伪代码昂
创建一个序列化类
简单使用
开发我们的Web API的第一件事是为我们的Web API提供一种将代码片段实例序列化和反序列化为诸如json
之类的表示形式的方式。我们可以通过声明与Django forms非常相似的序列化器(serializers)来实现。
models部分:
1 from django.db import models 2 3 # Create your models here. 4 5 6 class Book(models.Model): 7 title=models.CharField(max_length=32) 8 price=models.IntegerField() 9 pub_date=models.DateField() 10 publish=models.ForeignKey("Publish") 11 authors=models.ManyToManyField("Author") 12 def __str__(self): 13 return self.title 14 15 class Publish(models.Model): 16 name=models.CharField(max_length=32) 17 email=models.EmailField() 18 def __str__(self): 19 return self.name 20 21 class Author(models.Model): 22 name=models.CharField(max_length=32) 23 age=models.IntegerField() 24 def __str__(self): 25 return self.name
views部分:
1 from rest_framework.views import APIView 2 from rest_framework.response import Response 3 from .models import * 4 from django.shortcuts import HttpResponse 5 from django.core import serializers 6 7 8 from rest_framework import serializers 9 10 class BookSerializers(serializers.Serializer): 11 title=serializers.CharField(max_length=32) 12 price=serializers.IntegerField() 13 pub_date=serializers.DateField() 14 publish=serializers.CharField(source="publish.name") 15 #authors=serializers.CharField(source="authors.all") 16 authors=serializers.SerializerMethodField() 17 def get_authors(self,obj): 18 temp=[] 19 for author in obj.authors.all(): 20 temp.append(author.name) 21 return temp 22 23 24 class BookViewSet(APIView): 25 26 def get(self,request,*args,**kwargs): 27 book_list=Book.objects.all() 28 # 序列化方式1: 29 # from django.forms.models import model_to_dict 30 # import json 31 # data=[] 32 # for obj in book_list: 33 # data.append(model_to_dict(obj)) 34 # print(data) 35 # return HttpResponse("ok") 36 37 # 序列化方式2: 38 # data=serializers.serialize("json",book_list) 39 # return HttpResponse(data) 40 41 # 序列化方式3: 42 bs=BookSerializers(book_list,many=True) 43 return Response(bs.data)
ModelSerializer
class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" depth=1
提交post请求
def post(self,request,*args,**kwargs): 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)
重写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
urls部分:
urlpatterns = [ url(r'^books/$', views.BookViewSet.as_view(),name="book_list"), url(r'^books/(?P<pk>d+)$', views.BookDetailViewSet.as_view(),name="book_detail"), url(r'^publishers/$', views.PublishViewSet.as_view(),name="publish_list"), url(r'^publishers/(?P<pk>d+)$', views.PublishDetailViewSet.as_view(),name="publish_detail"), ]
分页
简单分页
1 from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination 2 3 class PNPagination(PageNumberPagination): 4 page_size = 1 5 page_query_param = 'page' 6 page_size_query_param = "size" 7 max_page_size = 5 8 9 class BookViewSet(viewsets.ModelViewSet): 10 11 queryset = Book.objects.all() 12 serializer_class = BookSerializers 13 def list(self,request,*args,**kwargs): 14 15 book_list=Book.objects.all() 16 pp=LimitOffsetPagination() 17 pager_books=pp.paginate_queryset(queryset=book_list,request=request,view=self) 18 print(pager_books) 19 bs=BookSerializers(pager_books,many=True) 20 21 #return Response(bs.data) 22 return pp.get_paginated_response(bs.data)
偏移分页
from rest_framework.pagination import LimitOffsetPagination