Django rest

  • REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
  • REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
  • REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
  • 所有的数据,无论是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
  • 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)

  REST是什么呢?它是一种架构风格,腾讯公司或其他公司建立API时要遵守的一种规则/风格,当然也有其他规则可以用。

  那么何为REST架构风格呢?首先我们来说说Web,因为rest是以web为平台的。我们知道,web是分布式信息系统为超文本文件和其他对象(资源)提供访问入口。

  在web上访问一个资源,需要3点:标识,表示,交互。通过这三个操作,又引出了三个概念:uri(包括url和urn),用来识别资源;representation(例如,图片,html,媒体)用来表示资源;通过协议与资源进行交互。所以,REST就是通过使用HTTP协议和URI,利用client/server对资源进行CRUD操作。

  那么为什么要使用REST设计呢?肯定是有它的优点的。

  1.客户端-服务端分离

   优点:提高用户界面的便携性,通过简化服务器提高可伸缩性....

  2..无状态(Stateless):从客户端的每个请求要包含服务器所需要的所有信息

    优点:提高可见性(可以单独考虑每个请求),提高了可靠性(更容易从局部故障中修复),提高可扩展性(降低了服务器资源使用)

1有状态与无状态区别: 2 如查询员工工资,如果查询工资是需要登陆系统,进入查询工资的页面,执行相关操作,获取工资的多少,则这种情况下是有状态的,因为查询工资的每一步操作都依赖于前一步操作,只需要前面操作不成功,后序操作就无法执行;如果输入一个url即可得到指定员工的工资,则这种情况下是无状态的,因为获取员工工资不依赖于其他资源或者状态,且这种情况下,员工工资是一个资源,由一个url与其对应,可以通过HTTPGET方法得到资源。

有状态与无状态区别

3.缓存(Cachable):服务器返回信息必须被标记是否可以缓存,如果缓存,客户端可能会重用之前的信息发送请求

   优点:减少交互次数,减少交互的平均延迟

  4.统一接口

   优点:提高交互的可见性,鼓励单独改善组件

  5.支持按需代码(Code-On-Demand 可选)

  优点:提高可扩展性

  下面来解释一下何为表征状态转移:

  举个例子:例如我订阅了一个人的博客,想要获取他发表的所有文章(这里『他发表的所有文章』就是一个资源Resource)。于是我就向他的服务发出请求,说『我要获取你发表的所有文章,最好是atom格式的』,这时候服务器向你返回了atom格式的文章列表第一页(这里『atom格式的文章列表』就是表征Representation)。

  你看到了第一页的页尾,想要看第二页,这时候有趣的事情就来了。如果服务器记录了应用的状态(stateful),那么你只要向服务询问『我要看下一页』,那么服务器自然就会返回第二页。类似的,如果你当前在第二页,想服务器请求『我要看下一页』,那就会得到第三页。但是REST的服务器恰恰是无状态的(stateless),服务器并没有保持你当前处于第几页,也就无法响应『下一页』这种具有状态性质的请求。因此客户端需要去维护当前应用的状态(application state),也就是『如何获取下一页资源』。当然,『下一页资源』的业务逻辑必然是由服务端来提供。服务器在文章列表的atom表征中加入一个URI超链接(hyper link),指向下一页文章列表对应的资源。客户端就可以使用**统一接口(Uniform Interface)的方式,从这个URI中获取到他想要的下一页文章列表资源。上面的『能够进入下一页』就是应用的状态(State)。服务器把『能够进入下一页』这个状态以atom表征形式传输(Transfer)给客户端就是表征状态传输(REpresentational State Transfer)**这个概念。

  REST是面向资源进行的,而资源是通过URI进行暴露的。

  URI 的设计只要负责把资源通过合理方式暴露出来就可以了。对资源的操作与它无关,操作是通过 HTTP动词来体现,所以REST 通过 URI 暴露资源时,会强调不要在 URI 中出现动词。

例如:比如:左边是错误的设计,而右边是正确的

1GET /rest/api/getDogs --> GET /rest/api/dogs 获取所有小狗狗 2GET /rest/api/addDogs --> POST /rest/api/dogs 添加一个小狗狗 3GET /rest/api/editDogs/:dog_id --> PUT /rest/api/dogs/:dog_id 修改一个小狗狗 4GET /rest/api/deleteDogs/:dog_id --> DELETE /rest/api/dogs/:dog_id 删除一个小狗狗

REST很好利用了HTTP本身就有的一些特征,如HTTP动词,HTTP状态码,HTTP报头等等。

更多参考:https://github.com/aisuhua/restful-api-design-references

注:上面的解释是看见比较好的博客copy下来的,

RESTful API设计

API与用户的通信协议,总是使用HTTPs协议

域名

https://api.example.com     尽量将API部署在专用域名(会存在跨域问题)

https://example.org/api/      API很简单

 版本

URL,如:https://api.example.com/v1/ 

请求头

跨域时,引发发送多次请求

路径

视网络上任何东西都是资源,均使用名词表示(可复数)
  https://api.example.com/v1/zoos
  https://api.example.com/v1/animals
  https://api.example.com/v1/employees

method

  • GET :      从服务器取出资源(一项或多项)
  • POST :   在服务器新建一个资源
  • PUT :      在服务器更新资源(客户端提供改变后的完整资源)
  • PATCH :  在服务器更新资源(客户端提供改变的属性)
  • DELETE :从服务器删除资源

过滤

通过在url上传参的形式传递搜索条件

状态码

  • 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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

更多状态码请看 http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

错误处理

状态码是4xx时,应返回错误信息,error当做key。

1{ 2  error: “Invalid API key” 3}

返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范。

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

Hypermedia API

RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。 

1{ 2 “link”: { 3 “rel”: “collection https://www.example.com/zoos“, 4 “href”: “https://api.example.com/zoos“, 5 “title”: “List of zoos”, 6 “type”: “application/vnd.yourformat+json” 7}}

 基于Django rest-framework框架的实现

先简单创建一个Django项目并且使用pip install djangorestframwork 安装django rest-framwork

要想快速实现的话可以点击这个快速实例

 序列化

在开始rest-framework说使用之前我们先说一说序列化

开发我们的Web API的第一件事是为我们的Web API提供一种将代码片段实例序列化和反序列化为诸如json之类的表示形式的方式。我们可以通过声明与Django forms非常相似的序列化器(serializers)来实现。

models部分:

1from django.db import models 2 3 4# Create your models here. 5class Book(models.Model): 6 title = models.CharField(max_length=32) 7 price = models.IntegerField() 8 pub_date = models.DateField() 9 publish = models.ForeignKey("Publish") 10 authors = models.ManyToManyField("Author") 11 12 def __str__(self): 13 return self.title 14 15 16class Publish(models.Model): 17 name = models.CharField(max_length=32) 18 email = models.EmailField() 19 20 def __str__(self): 21 return self.name 22 23 24class Author(models.Model): 25 name = models.CharField(max_length=32) 26 age = models.IntegerField() 27 28 def __str__(self): 29 return self.name

路由系统

1from django.conf.urls import url 2from django.contrib import admin 3from app01 import views 4 5urlpatterns = [ 6 url(r'^admin/', admin.site.urls), 7 url(r'^books/', views.bookView.as_view()), 8]

CBV视图(views)部分:

1from django.shortcuts import render, HttpResponse 2from app01.models import * 3from rest_framework.views import APIView 4from rest_framework.response import Response 5from rest_framework import serializers 6# 为queryset,model对象做序列化===》相当于form组件使用 7# 这个可以放在一个单独的.py文件中 8class BookSerializers(serializers.Serializer): 9 id = serializers.IntegerField() 10 title = serializers.CharField(max_length=32) 11 price = serializers.IntegerField() 12 pub_date = serializers.DateField() 13 # 上面的只是针对的一对一字段的,若果用在一对多字段上的时候就会输出关联那张表的 def __str__(self) 14 # publish = serializers.CharField() 15 # 这个表示的是实现关联表中publish表中的name字段,可以自己定制 16 publish = serializers.CharField(source="publish.name") 17 # authors=serializers.CharField(source="authors.all") 18 # 当出现多对多的时候上面的方式也不可行,上面会显示的是一个queryset对象 19 # 自己定制显示什么 20 authors = serializers.SerializerMethodField() 21 22 def get_authors(self, obj): 23 temp = [] 24 for obj in obj.authors.all(): 25 temp.append(obj.name) 26 return temp 27 28 29# 这个相当于modelform组件一样 30class BookModelSerializers(serializers.ModelSerializer): 31 class Meta: 32 model = Book 33 fields = '__all__' 34 depth = 1 35 36 # 当遇到一对多字段的时候 37 publish = serializers.CharField(source='publish.name') 38 # 多对多字段 39 authors = serializers.SerializerMethodField() 40 41 def get_authors(self, obj): 42 temp = [] 43 for obj in obj.authors.all(): 44 temp.append(obj.name) 45 return temp 46 47 48# Create your views here. 49class BookViewSet(APIView): 50 def get(self, request, *args, **kwargs): 51 # 序列化 52 # 方式一: 53 # book_data = list(Book.objects.all().values('name', 'email')) 54 # return HttpResponse(book_data) 55 # 方式二: 56 # from django.forms.models import model_to_dict 57 # book_data = Book.objects.all() 58 # temp = [] 59 # for obj in book_data: 60 # temp.append(model_to_dict(obj)) 61 # return HttpResponse(temp) 62 # 方式三: 63 # from django.core import serializers 64 # book_data = Book.objects.all() 65 # ret = serializers.serialize("json", book_data) 66 # print(type(ret)) 67 # return HttpResponse(ret) 68 # 方式四:序列组件 69 # 这里面和Django里面的form组件和modelform组件相似 70 # 这里记住要是使用浏览器访问的话这个必须要在setting中的INSTALLED_APPS注册rest_framework要不就会报错 71 # 最好在项目一开始的时候就在setting里面注册 72 book_data = Book.objects.all() 73 # many=True 表示的queryset对象,反之many=False就表示为model对象 74 # 相当于form组件 75 # bs = BookSerializers(book_data, many=True) 76 bs = BookModelSerializers(book_data, many=True) 77 return Response(bs.data)

记住要把在setting里面注册restframework。

1INSTALLED_APPS = [ 2 'django.contrib.admin', 3 'django.contrib.auth', 4 'django.contrib.contenttypes', 5 'django.contrib.sessions', 6 'django.contrib.messages', 7 'django.contrib.staticfiles', 8 'app01.apps.App01Config', 9 'rest_framework' 10]

serializers

在app01/utils/serializers.py 写serializers代码(类似于form)

1from rest_framework import serializers 2 3 4# 为queryset,model对象做序列化===》相当于form组件使用 5class BookSerializers(serializers.Serializer): 6 id = serializers.IntegerField() 7 title = serializers.CharField(max_length=32) 8 price = serializers.IntegerField() 9 pub_date = serializers.DateField() 10 # 上面的只是针对的一对一字段的,若果用在一对多字段上的时候就会输出关联那张表的 def __str__(self) 11 # publish = serializers.CharField() 12 # 这个表示的是实现关联表中publish表中的name字段,可以自己定制 13 publish = serializers.CharField(source="publish.name") 14 # authors=serializers.CharField(source="authors.all") 15 # 当出现多对多的时候上面的方式也不可行,上面会显示的是一个queryset对象 16 # 自己定制显示什么 17 authors = serializers.SerializerMethodField() 18 19 def get_authors(self, obj): 20 temp = [] 21 for obj in obj.authors.all(): 22 temp.append(obj.name) 23 return temp

 ModelSerializer

在app01/utils/serializers.py 写ModelSerializer代码(类似于modelform)

1from app01.models import * # 导入表 2from rest_framework import serializers 3 4# 这个相当于modelform组件一样 5class BookModelSerializers(serializers.ModelSerializer): 6 class Meta: 7 model = Book 8 fields = '__all__' 9 # 当遇到一对多字段的时候 10 publish = serializers.CharField(source='publish.name') 11 # 多对多字段 12 authors = serializers.SerializerMethodField() 13 14 def get_authors(self, obj): 15 temp = [] 16 for obj in obj.authors.all(): 17 temp.append(obj.name) 18 return temp

当一个model有外键的时候,默认显示的是外键的id,此时要显示外键的所有值可以用下面,depth,会把外键的所有值显示出来,depth应该是整数,表明嵌套的层级数量。

1class BookModelSerializers(serializers.ModelSerializer): 2 class Meta: 3 model = Book 4 fields = '__all__' 5 # 上面表示显示所有字段也可以只显示我们想要的 6 # fields = ('id',) ,表示只显示id 7 # 要是不想显示哪个字段就可以使用 8 # exclude=('id',), 表示出了id其他的都显示 9 depth = 1

上面就是关于get请求获取所有的数据。

同样 ModelSerializer也支持全局钩子和局部钩子,和form组件一样局部钩子为validate_字段名(form组件为clean_字段名),

全局钩子为validate。

提交post请求

路由系统

1from django.conf.urls import url 2from django.contrib import admin 3from app01 import views 4 5urlpatterns = [ 6 url(r'^admin/', admin.site.urls), 7 url(r'^books/', views.BookViewSet.as_view(), name="book"), 8]

在app01/utils/serializers.py 里面的代码

1from rest_framework import serializersfrom app01.models import * 2 3class BookModelSerializers(serializers.ModelSerializer): 4 class Meta: 5 model = Book 6 fields = '__all__' 7 8 publish = serializers.HyperlinkedIdentityField( 9 view_name="detail_publish", 10 lookup_field="publish_id", 11 lookup_url_kwarg="pk", 12 ) 13 14 # 当遇到一对多字段的时候 15 # source='publish.name'这个后面是publish.name所以我们前面传回来的应该是publish.name对应的名字 16 # # 如果是publish.pk 前面传过来的就是 publish.pk对应的id 17 publish = serializers.CharField(source='publish.name') 18 # 多对多字段 19 authors = serializers.SerializerMethodField() 20 21 def get_authors(self, obj): 22 temp = [] 23 for obj in obj.authors.all(): 24 temp.append(obj.name) 25 return temp 26 27 # 因为上面自己定制了publish原来的save不支持这样保存,所以下面要重写create方法 28 def create(self, validated_data): 29 print(validated_data) 30 # 如果上面定制的publish.name 改为 publish.pk下面这2行就不用写了 31 if not validated_data["publish"]["name"].isdigit(): 32 publish_id = Book.objects.filter(publish__name=validated_data["publish"]["name"]).values('publish_id').first() 33 validated_data['publish_id'] = publish_id['publish_id'] 34 else: 35 validated_data['publish_id']=validated_data["publish"]["name"] 36 # 其实前端一般都会传数字不会传汉字, 所以可以向下面这样写 37 # validated_data['publish_id'] = validated_data["publish"]["name"] 38 validated_data.pop('publish') 39 authors = validated_data.pop('authors') 40 book = Book.objects.create(**validated_data) 41 book.authors.add(*authors) 42 return book 43 # 44 def update(self, instance, validated_data): 45 print('validated_data', validated_data) 46 if not validated_data["publish"]["name"].isdigit(): 47 publish_id = Book.objects.filter(publish__name=validated_data["publish"]["name"]).values('publish_id').first() 48 validated_data['publish_id'] = publish_id['publish_id'] 49 else: 50 validated_data['publish_id']=validated_data["publish"]["name"] 51 validated_data.pop('publish') 52 authors = validated_data.pop('authors') 53 instance.update(**validated_data) # 更新普通字段和一对多字段 54 nid = instance.values('id')[0]['id'] 55 instance.first().authors.set(authors) # 更新多对多字段 56 book_data = Book.objects.filter(id=nid).first() 57 58 # 使用下面的放法前面传进来的instance是obj对象,而这里传进来的queryset对象像下面出入一样 59 # book_obj = Book.objects.filter(pk=pk).first() 60 # bs = BookModelSerializers(book_obj, data=request.data) 61 # instance.authors.set(authors) 62 # book_data = super().update(instance, validated_data 63 return book_data

CBV视图(view)部分

1from django.shortcuts import render, HttpResponse 2from app01.models import * 3from rest_framework.views import APIView 4from rest_framework.response import Response 5from rest_framework import serializers 6# 把Serializers和ModelSerializers单独放在一个py文件中 7from app01.utils.serilizer import * 8import json 9# Create your views here. 10class BookViewSet(APIView): 11 def get(self, request, *args, **kwargs): 12 book_data = Book.objects.all() 13 # many=True 表示的queryset对象,反之many=False就表示为model对象 14 # 相当于form组件 15 # bs = BookSerializers(book_data, many=True) 16 # 这个就是相当于modelform组件, 17 bs = BookModelSerializers(book_data, many=True) 18 return Response(bs.data) 19 20 def post(self, request, *args, **kwargs): 21 bs = BookModelSerializers(data=request.data) 22 if bs.is_valid(): 23 # 直接把这个传到后面就不需要做验证了,对于多对多关系提交自定制显示 24 bs.save(authors=request.data['authors']) 25 return Response(bs.data) 26 else: 27 return Response(bs.errors) 28 29 30class BookDetailViewSet(APIView): 31 def get(self, request, pk): 32 book_obj = Book.objects.filter(pk=pk).first() 33 bs = BookModelSerializers(book_obj, context={'request': request}) 34 return Response(bs.data) 35 36 def put(self, request, pk): 37 book_obj = Book.objects.filter(pk=pk) 38 # 对应serilizer里面的 39 # book_obj = Book.objects.filter(pk=pk).first() 40 # 这里如果是一对多或者多对多的自定制的话就需要自己写update方法 41 bs = BookModelSerializers(book_obj, data=request.data) 42 if bs.is_valid(): 43 bs.save(authors=request.data['authors']) 44 return Response(bs.data) 45 else: 46 return Response(bs.errors) 47 48 def delete(self, request, pk): 49 Book.objects.filter(pk=pk).delete() 50 return Response()

单条数据的get和put以及delet

class BookDetailViewSet(APIView):    

     def get(self, request, pk):

1 book_obj = Book.objects.filter(pk=pk).first() 2 bs = BookModelSerializers(book_obj) 3 return Response(bs.data) 4 5 def put(self, request, pk): 6 book_obj = Book.objects.filter(pk=pk) 7 # 这里如果是一对多或者多对多的自定制的话就需要自己写update方法 8 bs = BookModelSerializers(book_obj, data=request.data) 9 if bs.is_valid(authors=request.data['authors']):       bs.save() return Response(bs.data) else: return Response(bs.errors) def delete(self, request, pk): Book.objects.filter(pk=pk).delete() return Response()

超链接API:Hyperlinked

1class BookModelSerializers(serializers.ModelSerializer): 2 class Meta: 3 model = Book 4 fields = '__all__' 5 6 publish = serializers.HyperlinkedIdentityField( 7 view_name="detail_publish", 8 lookup_field="publish_id", 9 lookup_url_kwarg="pk", 10 )

上面的黄色字段就是超链接的关键,

view_name表示的是该路由的别名获取到publish/(?P<pk>\d+)/

lookup_field 表示在这个序列化里面获取到pk的值(也就是一个字段publish_id)

lookup_url_kwarg 表示把上面获取到的pk值放到view_name的(?P<pk>\d+)里面

在CBV相应部分

1class BookDetailViewSet(APIView): 2 def get(self, request, pk): 3 book_obj = Book.objects.filter(pk=pk).first() 4 bs = BookModelSerializers(book_obj, context={'request': request}) 5 return Response(bs.data)

主要是在后面添加黄色部位

路由系统为

1urlpatterns = [ 2 url(r'^admin/', admin.site.urls), 3 url(r'^books/$', views.BookViewSet.as_view(), name="book"), 4 5 url(r'^books/(?P<pk>\d+)/$', views.BookDetailViewSet.as_view(), name='detail_book'), 6 url(r'^publish/(?P<pk>\d+)/$', views.PublishDetailViewSet.as_view(), name='detail_publish'), 7]

试图三部曲

mixin类编写试图

1from rest_framework import mixins 2from rest_framework import genericsfrom app01.utils.serializers import * 3 4class 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 19class 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模块。

1from rest_framework import mixins 2from rest_framework import genericsfrom app01.utils.serializers import * 3 4class BookViewSet(generics.ListCreateAPIView): 5 queryset = Book.objects.all() 6 serializer_class = BookModelSerializers 7 8class BookDetailViewSet(generics.RetrieveUpdateDestroyAPIView): 9 queryset = Book.objects.all() 10 serializer_class = BookModelSerializers 11 12class PublishViewSet(generics.ListCreateAPIView): 13 queryset = Publish.objects.all() 14 serializer_class = PublshModelSerializers 15 16class PublishDetailViewSet(generics.RetrieveUpdateDestroyAPIView): 17 queryset = Publish.objects.all() 18 serializer_class = PublshModelSerializers

viewsets.ModelViewSet(最终版)

urls.py

1from django.conf.urls import url 2from django.contrib import admin 3from app01 import views 4 5urlpatterns = [ 6 url(r'^admin/', admin.site.urls), 7 url(r'^books/$', views.BookViewSet.as_view({"get": "list", "post": "create"}), name="book_list"), 8 url(r'^books/(?P<pk>\d+)$', views.BookViewSet.as_view({ 9 'get': 'retrieve', 10 'put': 'update', 11 'patch': 'partial_update', 12 'delete': 'destroy' 13 }), name="book_detail"), 14]

views.py

1from rest_framework import viewsets 2from app01.models import *from app01.utils.serializers import * 3class BookViewSet(viewsets.ModelViewSet): 4 queryset = Book.objects.all() 5 # BookModelSerializers和前面的一样没什么变化功能类似于modelform 6 serializer_class = BookModelSerializers

认证

 基于token的用户认证以及局部认证组件

沿用上面的

token:服务端动态生成的1串用来检验用户身份的字符串,可以放在header、cokies、url参数(安全性较差)、请求体(CSRF token);

token和session类似,不同于 session的是token比较灵活,不仅仅可以cokies里

url.py的代码

1from django.conf.urls import url 2from django.contrib import admin 3from app01 import views 4 5urlpatterns = [ 6 url(r'^admin/', admin.site.urls), 7 url(r'^login/$', views.LoginView.as_view(), name="login"), 8 9 url(r'^books/$', views.BookModelView.as_view({"get": "list", "post": "create"}), name="books"), 10 url(r'^books/(?P<pk>\d+)/$', views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}),name="detail_book"), 11 12 url(r'^authors/$', views.AuthorModelView.as_view({"get": "list", "post": "create"}), name="author"), 13 url(r'^authors/(?P<pk>\d+)/$',views.AuthorModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_author"), 14 15 url(r'^publishers/$', views.PublisherModelView.as_view({"get": "list", "post": "create"}), name="publishers"), 16 url(r'^publishers/(?P<pk>\d+)/$',views.PublisherModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_publishers"), 17 18]

url.py

app01里面的models.py的代码

1from django.db import models 2 3 4# Create your models here. 5class UserInfo(models.Model): 6 USER_TYPE = ( 7 (1, '普通用户'), 8 (2, 'VIP'), 9 (3, 'SVIP') 10 ) 11 12 user_type = models.IntegerField(choices=USER_TYPE, default=1) 13 username = models.CharField(max_length=32) 14 password = models.CharField(max_length=64) 15 16 def __str__(self): 17 return self.username 18 19# 设置 one to one 1个用户不能在不同设备上登录 20# 设置 Freikey 支持1个用户 在不同设备上同时登录 21class UserToken(models.Model): 22 user = models.OneToOneField(UserInfo, on_delete=models.CASCADE) 23 token = models.CharField(max_length=64) 24 25 def __str__(self): 26 return self.token 27 28 29class Book(models.Model): 30 title = models.CharField(max_length=32) 31 price = models.IntegerField() 32 pub_date = models.DateField() 33 publish = models.ForeignKey("Publish") 34 authors = models.ManyToManyField("Author") 35 36 def __str__(self): 37 return self.title 38 39 40class Publish(models.Model): 41 name = models.CharField(max_length=32) 42 email = models.EmailField() 43 44 def __str__(self): 45 return self.name 46 47 48class Author(models.Model): 49 name = models.CharField(max_length=32) 50 age = models.IntegerField() 51 52 def __str__(self): 53 return self.name

model.py

CBV视图里面的代码

1from django.shortcuts import render 2from rest_framework import viewsets 3from rest_framework.views import APIView 4from app01 import models 5from django.http import JsonResponse 6from app01.utils.auth import CustomAuthentication 7from app01.utils.serializers import BookModelSerializers,PublishModelSerializers,AuthorModelSerializers 8 9 10# Create your views here. 11def md5(user): 12 import hashlib 13 import time 14 # 当前时间,相当于生成一个随机的字符串 15 ctime = str(time.time()) 16 m = hashlib.md5(bytes(user, encoding='utf-8')) 17 m.update(bytes(ctime, encoding='utf-8')) 18 return m.hexdigest() 19 20 21class LoginView(APIView): 22 """60s内任何人都只可以登录3次""" 23 authentication_classes = [] 24 permission_classes = [] 25 26 def post(self, request, *args, **kwargs): 27 ret = {'code': 1000, 'msg': None} 28 try: 29 user = request._request.POST.get('username') 30 pwd = request._request.POST.get('password') 31 obj = models.UserInfo.objects.filter(username=user, password=pwd).first() 32 if not obj: 33 ret['code'] = 1001 34 ret['msg'] = '用户名或密码错误' 35 # 为用户创建token 36 token = md5(user) 37 # 存在就更新,不存在就创建 38 models.UserToken.objects.update_or_create(user=obj, defaults={'token': token}) 39 ret['token'] = token 40 except Exception as e: 41 ret['code'] = 1002 42 ret['msg'] = '请求异常' 43 return JsonResponse(ret) 44 45 46class StartAuthentication(): 47 """ 48 局部使用认证功能的情况下,优先继承该类 49 """ 50 authentication_classes = [CustomAuthentication] 51 52 53class AuthorModelView(StartAuthentication,viewsets.ModelViewSet): 54 """不管有没有登陆,都可以查看且60内只能看三次""" 55 # 如果不使用继承类来实现局部认证就可以使用下面情况 56 # authentication_classes = [CustomAuthentication] 57 queryset = models.Author.objects.all() 58 serializer_class = AuthorModelSerializers

Views.py

app01/utils/auth.py 里面的代码

1from rest_framework import authentication,exceptions 2from app01.models import UserToken 3 4 5class CustomAuthentication(authentication.BaseAuthentication): 6 def authenticate(self, request): 7 token = request.GET.get('token') 8 token_obj = UserToken.objects.filter(token=token).first() 9 if not token_obj: 10 raise exceptions.AuthenticationFailed('验证失败') 11 return (token_obj.user, token_obj.token) 12 13 def authenticate_header(self, request): 14 pass

auth.py

上面的局部认证只要在相应的类里面继承(StartAuthentication)就行了

全局视图认证组件

只要在settings.py配置

1REST_FRAMEWORK={ 2 3 "DEFAULT_AUTHENTICATION_CLASSES":["resdemo.service.auth.CustomAuthentication"] 4}

上面黄色部位就是你的token认证组件的位置(也就是上面的author.py里面的CustomAuthentication类

如果要某一个不需要认证就在其中添加

authentication_classes = []    #里面为空,代表不需要认证,这个就是优先执行自己的,不执行全局的,因为自己的为空所以就是不要认证。

如上所示我们配置了全局模式当我们有一个不需要认证的时候就直接在这里面加上 authentication_classes = []

使用RestAPI认证功能小结

1、创建2张表userinfo 和usertoken表

2、认证类的authenticate方法去请求头中获取token信息,然后去token表中查询token是否存在;

3、查询到token 是正常用户(返回 用户名)否则为匿名用户(raise异常终止认证、或者 return none进行下一个认证)

4、局部应用

方式1::哪个CBV需要认证在类中定义authentication_classes =[CustomAuthentication ] 

方式2:额外定义1个类,CBV多继承

方式3:全局配置使用认证功能,那个CBV不使用authentication_classes =[ ]  

5、全局使用 在配置文件中配置 ,注意重新创建一个模块,把认证类放里面;

自己写认证方法总结:

1、创建认证类

  • 继承BaseAuthentication    --->>1.重写authenticate方法;2.authenticate_header方法直接写pass就可以(这个方法必须写)                                                                                                           继承的类在 from rest_framework import authentication中authentication.BaseAuthentication

2、authenticate()方法返回值(三种)

  • None ----->>>当前认证不管,等下一个认证来执行
  • raise exceptions.AuthenticationFailed('用户认证失败')       # from rest_framework import exceptions
  •  有返回值元祖形式:(元素1,元素2)      #元素1复制给request.user;  元素2复制给request.auth

3、需要创建导入的类

1from rest_framework import authentication,exceptions 2from app01.models import UserToken

权限组件

 添加权限

1、在app01/utils/permission.py的文件中的代码

1#!/usr/bin/evn python 2#-*-coding:utf-8-*- 3from rest_framework import permissions 4class SVIPPermission(permissions.BasePermission): 5 message = "不是SVIP不给查看" # 当没有权限的时候提示信息 6 7 def has_permission(self, request, view): 8 if request.user.user_type != 3: 9 return False 10 return True 11 12 13class MyPermission(permissions.BasePermission): 14 message = "普通用户不给查看" 15 16 def has_permission(self, request, view): 17 if request.user.user_type == 1: 18 return False 19 return True

permission

2、在setting里面设置全局权限

1REST_FRAMEWORK = { 2 "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.auth.TokenAuth"], 3 "DEFAULT_PERMISSION_CLASSES": ["app01.utils.permission.SVIPPermission"], 4}

如果某一个视图不想要权限的话就可以在该视图中加 permission_class = []

或者不执行全局的权限只执行自己想要的权限

3、views.py 添加权限

1from django.shortcuts import render 2from rest_framework import viewsets 3from rest_framework.views import APIView 4from app01 import models 5from django.http import JsonResponse 6from app01.utils.auth import CustomAuthentication 7from app01.utils.serializers import BookModelSerializers,PublishModelSerializers,AuthorModelSerializers 8from app01.utils.permission import MyPermission 9 10 11# Create your views here. 12def md5(user): 13 import hashlib 14 import time 15 # 当前时间,相当于生成一个随机的字符串 16 ctime = str(time.time()) 17 m = hashlib.md5(bytes(user, encoding='utf-8')) 18 m.update(bytes(ctime, encoding='utf-8')) 19 return m.hexdigest() 20 21 22class LoginView(APIView): 23 """60s内任何人都只可以登录3次""" 24 authentication_classes = [] 25 permission_classes = [] 26 27 def post(self, request, *args, **kwargs): 28 ret = {'code': 1000, 'msg': None} 29 try: 30 user = request._request.POST.get('username') 31 pwd = request._request.POST.get('password') 32 obj = models.UserInfo.objects.filter(username=user, password=pwd).first() 33 if not obj: 34 ret['code'] = 1001 35 ret['msg'] = '用户名或密码错误' 36 # 为用户创建token 37 token = md5(user) 38 # 存在就更新,不存在就创建 39 models.UserToken.objects.update_or_create(user=obj, defaults={'token': token}) 40 ret['token'] = token 41 except Exception as e: 42 ret['code'] = 1002 43 ret['msg'] = '请求异常' 44 return JsonResponse(ret) 45 46 47class StartAuthentication(): 48 """ 49 局部使用认证功能的情况下,优先继承该类 50 """ 51 authentication_classes = [CustomAuthentication] 52 53 54class AuthorModelView(viewsets.ModelViewSet): 55 """不管有没有登陆,都可以查看且""" 56 # 不执行认证和权限认证 57 authentication_classes = [] 58 permission_classes = [] 59 queryset = models.Author.objects.all() 60 serializer_class = AuthorModelSerializers 61 62 63class BookModelView(viewsets.ModelViewSet): 64 """登陆后SVIP用户可以查看""" 65 # 执行全局的权限和认证,全局的权限是只能SVIP用户可以访问 66 queryset = models.Book.objects.all() 67 serializer_class = BookModelSerializers 68 69 70class PublisherModelView(viewsets.ModelViewSet): 71 """登陆后普通用户不可以看""" 72 # 不执行全局的权限,只执行MyPermission这个权限 73 permission_classes = [MyPermission] 74 queryset = models.Publish.objects.all() 75 serializer_class = PublishModelSerializers

views.py

4、url.py

1from django.conf.urls import url 2from django.contrib import admin 3from app01 import views 4 5urlpatterns = [ 6 url(r'^admin/', admin.site.urls), 7 url(r'^login/$', views.LoginView.as_view(), name="login"), 8 9 url(r'^books/$', views.BookModelView.as_view({"get": "list", "post": "create"}), name="books"), 10 url(r'^books/(?P<pk>\d+)/$', views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}),name="detail_book"), 11 12 url(r'^authors/$', views.AuthorModelView.as_view({"get": "list", "post": "create"}), name="author"), 13 url(r'^authors/(?P<pk>\d+)/$',views.AuthorModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_author"), 14 15 url(r'^publishers/$', views.PublisherModelView.as_view({"get": "list", "post": "create"}), name="publishers"), 16 url(r'^publishers/(?P<pk>\d+)/$',views.PublisherModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_publishers"), 17 18]

url.py

5、app01/utils/auth.py的代码上面有不需要改变的,这里就不写了

总结:

(1)使用

  • 自己写的权限类:1.必须继承BasePermission类;  2.必须实现:has_permission方法
  • 这个继承的类在  from rest_framework import permission中permission.BaseAuthentication

(2)返回值

  • True   有权访问
  • False  无权访问

(3)局部

  • permission_classes = [MyPremission,] 

 (4)全局

1REST_FRAMEWORK = { 2 #权限 3 "DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.SVIPPremission'], 4}

节流

throttle(访问频率)组件

在app01.utils.throttle.py里面的代码

1from rest_framework import throttling 2import time 3 4VISIT_RECORD = {} 5 6 7class VisitThrottle(throttling.BaseThrottle): 8 """60s内只能访问三次""" 9 10 def __init__(self): 11 self.history = None # 初始化访问记录 12 13 def allow_request(self, request, view): 14 remote_addr = self.get_ident(request) 15 ctime = time.time() 16 # 如果当前IP不在访问记录里面,就添加到记录 17 if remote_addr not in VISIT_RECORD: 18 VISIT_RECORD[remote_addr] = [ctime, ] # 键值对的形式保存 19 return True # True表示可以访问 20 # 获取当前ip的历史访问记录 21 history = VISIT_RECORD.get(remote_addr) 22 self.history = history 23 # 如果有历史访问记录,并且最早一次的访问记录离当前时间超过60s,就删除最早的那个访问记录, 24 # 只要为True,就一直循环删除最早的一次访问记录 25 # 结合下面的3次可以知道当我们事件 26 while history and history[-1] < ctime - 60: # 最早的访问一次加60s小于当前时间 27 history.pop() 28 # 如果访问记录不超过三次,就把当前的访问记录插到第一个位置(pop删除最后一个) 29 if len(history) < 3: 30 history.insert(0, ctime) 31 return True 32 else: 33 return False 34 35 def wait(self): 36 '''还需要等多久才能访问''' 37 ctime = time.time() 38 return 60 - (ctime - self.history[-1])

thorttle.py

 在views.py的代码

1from django.shortcuts import render 2from rest_framework import viewsets 3from rest_framework.views import APIView 4from app01 import models 5from django.http import JsonResponse 6from app01.utils.auth import CustomAuthentication 7from app01.utils.serializers import BookModelSerializers,PublishModelSerializers,AuthorModelSerializers 8from app01.utils.permission import MyPermission 9 10 11# Create your views here. 12def md5(user): 13 import hashlib 14 import time 15 # 当前时间,相当于生成一个随机的字符串 16 ctime = str(time.time()) 17 m = hashlib.md5(bytes(user, encoding='utf-8')) 18 m.update(bytes(ctime, encoding='utf-8')) 19 return m.hexdigest() 20 21 22class LoginView(APIView): 23 """60s内任何人都只可以登录3次""" 24 authentication_classes = [] 25 permission_classes = [] 26 27 def post(self, request, *args, **kwargs): 28 ret = {'code': 1000, 'msg': None} 29 try: 30 user = request._request.POST.get('username') 31 pwd = request._request.POST.get('password') 32 obj = models.UserInfo.objects.filter(username=user, password=pwd).first() 33 if not obj: 34 ret['code'] = 1001 35 ret['msg'] = '用户名或密码错误' 36 # 为用户创建token 37 token = md5(user) 38 # 存在就更新,不存在就创建 39 models.UserToken.objects.update_or_create(user=obj, defaults={'token': token}) 40 ret['token'] = token 41 except Exception as e: 42 ret['code'] = 1002 43 ret['msg'] = '请求异常' 44 return JsonResponse(ret) 45 46 47class StartAuthentication(): 48 """ 49 局部使用认证功能的情况下,优先继承该类 50 """ 51 authentication_classes = [CustomAuthentication] 52 53 54class AuthorModelView(viewsets.ModelViewSet): 55 """不管有没有登陆,都可以查看且60内只能看三次""" 56 # 不执行认证和权限认证 57 authentication_classes = [] 58 permission_classes = [] 59 queryset = models.Author.objects.all() 60 serializer_class = AuthorModelSerializers 61 62 63class BookModelView(viewsets.ModelViewSet): 64 """登陆后SVIP用户可以查看不限次数""" 65 # 执行全局的权限和认证,全局的权限是只能SVIP用户可以访问 66 throttle_classes = [] 67 queryset = models.Book.objects.all() 68 serializer_class = BookModelSerializers 69 70 71class PublisherModelView(viewsets.ModelViewSet): 72 """登陆后普通用户不可以看且60内只能看三次""" 73 # 不执行全局的权限,只执行MyPermission这个权限 74 permission_classes = [MyPermission] 75 queryset = models.Publish.objects.all() 76 serializer_class = PublishModelSerializers

Views.py

settings中全局配置节流

1REST_FRAMEWORK = { 2 #节流 3 "DEFAULT_THROTTLE_CLASSES":['app01.utils.throttle.VisitThrottle'], 4}

内置的节流类

(1)throttle.py

1from rest_framework import throttling 2 3 4class VisitThrottle2(throttling.SimpleRateThrottle): 5 '''匿名用户60s只能访问三次(根据ip)''' 6 scope = 'NBA' # 这里面的值,自己随便定义,settings里面根据这个值配置Rate 7 8 def get_cache_key(self, request, view): 9 # 通过ip限制节流 10 return self.get_ident(request) 11 12 13class UserThrottle(throttling.SimpleRateThrottle): 14 '''登录用户60s可以访问10次''' 15 scope = 'NBAUser' # 这里面的值,自己随便定义,settings里面根据这个值配置Rate 16 17 def get_cache_key(self, request, view): 18 return request.user.username

(2)settings.py

1#全局 2REST_FRAMEWORK = { 3 #节流 4 "DEFAULT_THROTTLE_CLASSES":['app01.utils.throttle.UserThrottle'], #全局配置,登录用户节流限制(10/m) 5 "DEFAULT_THROTTLE_RATES":{ 6 'NBA':'3/m', #没登录用户3/m,NBA就是scope定义的值 7 'NBAUser':'10/m', #登录用户10/m,NBAUser就是scope定义的值 8 } 9}

(3) views.py

1from django.shortcuts import render 2from rest_framework import viewsets 3from rest_framework.views import APIView 4from app01 import models 5from django.http import JsonResponse 6from app01.utils.auth import CustomAuthentication 7from app01.utils.serializers import BookModelSerializers,PublishModelSerializers,AuthorModelSerializers 8from app01.utils.permission import MyPermission 9from app01.utils.throttle import VisitThrottle2 10 11 12# Create your views here. 13def md5(user): 14 import hashlib 15 import time 16 # 当前时间,相当于生成一个随机的字符串 17 ctime = str(time.time()) 18 m = hashlib.md5(bytes(user, encoding='utf-8')) 19 m.update(bytes(ctime, encoding='utf-8')) 20 return m.hexdigest() 21 22 23class LoginView(APIView): 24 """60s内任何人都只可以登录3次""" 25 authentication_classes = [] 26 permission_classes = [] 27 28 def post(self, request, *args, **kwargs): 29 ret = {'code': 1000, 'msg': None} 30 try: 31 user = request._request.POST.get('username') 32 pwd = request._request.POST.get('password') 33 obj = models.UserInfo.objects.filter(username=user, password=pwd).first() 34 if not obj: 35 ret['code'] = 1001 36 ret['msg'] = '用户名或密码错误' 37 # 为用户创建token 38 token = md5(user) 39 # 存在就更新,不存在就创建 40 models.UserToken.objects.update_or_create(user=obj, defaults={'token': token}) 41 ret['token'] = token 42 except Exception as e: 43 ret['code'] = 1002 44 ret['msg'] = '请求异常' 45 return JsonResponse(ret) 46 47 48class StartAuthentication(): 49 """ 50 局部使用认证功能的情况下,优先继承该类 51 """ 52 authentication_classes = [CustomAuthentication] 53 54 55class AuthorModelView(viewsets.ModelViewSet): 56 """不管有没有登陆,都可以查看且60内只能看三次使用的是自定制的节流""" 57 # 不执行认证和权限认证 58 authentication_classes = [] 59 permission_classes = [] 60 # 因为全局设置的是登录用户(10/m),这里不需要登录,所以就是用匿名用户ip限流(3/m) 61 throttle_classes = [VisitThrottle2] 62 queryset = models.Author.objects.all() 63 serializer_class = AuthorModelSerializers 64 65 66class BookModelView(viewsets.ModelViewSet): 67 """登陆后SVIP用户可以查看不限次数""" 68 # 执行全局的权限和认证,全局的权限是只能SVIP用户可以访问 69 throttle_classes = [] 70 queryset = models.Book.objects.all() 71 serializer_class = BookModelSerializers 72 73 74class PublisherModelView(viewsets.ModelViewSet): 75 """登陆后普通用户不可以看,且SVIP用户60s内能看10次使用的是内置的节流""" 76 # 不执行全局的权限,只执行MyPermission这个权限 77 permission_classes = [MyPermission] 78 queryset = models.Publish.objects.all() 79 serializer_class = PublishModelSerializers

views.py

(4) 里面的认证和权限和上面的一样

说明:

  • API.utils.throttle.UserThrottle   这个是全局配置(根据ip限制,10/m)
  • DEFAULT_THROTTLE_RATES      --->>>设置访问频率的
  • throttle_classes = [VisitThrottle,]     --->>>局部配置(不实用settings里面默认的全局配置,启用现在的配置)

所有的认证、权限、节流的代码

1from django.db import models 2 3 4# Create your models here. 5class UserInfo(models.Model): 6 USER_TYPE = ( 7 (1, '普通用户'), 8 (2, 'VIP'), 9 (3, 'SVIP') 10 ) 11 12 user_type = models.IntegerField(choices=USER_TYPE, default=1) 13 username = models.CharField(max_length=32) 14 password = models.CharField(max_length=64) 15 16 def __str__(self): 17 return self.username 18 19# 设置 one to one 1个用户不能在不同设备上登录 20# 设置 Freikey 支持1个用户 在不同设备上同时登录 21class UserToken(models.Model): 22 user = models.OneToOneField(UserInfo, on_delete=models.CASCADE) 23 token = models.CharField(max_length=64) 24 25 def __str__(self): 26 return self.token 27 28 29class Book(models.Model): 30 title = models.CharField(max_length=32) 31 price = models.IntegerField() 32 pub_date = models.DateField() 33 publish = models.ForeignKey("Publish") 34 authors = models.ManyToManyField("Author") 35 36 def __str__(self): 37 return self.title 38 39 40class Publish(models.Model): 41 name = models.CharField(max_length=32) 42 email = models.EmailField() 43 44 def __str__(self): 45 return self.name 46 47 48class Author(models.Model): 49 name = models.CharField(max_length=32) 50 age = models.IntegerField() 51 52 def __str__(self): 53 return self.name

model.py

1REST_FRAMEWORK = { 2 "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.auth.CustomAuthentication"], 3 "DEFAULT_PERMISSION_CLASSES": ["app01.utils.permission.SVIPPermission"], 4 "DEFAULT_THROTTLE_CLASSES": ['app01.utils.throttle.UserThrottle'], # 全局配置,登录用户节流限制(10/m) 5 "DEFAULT_THROTTLE_RATES": { 6 'NBA': '4/m', # 没登录用户4/m(表示60s4次)NBA就是scope定义的值 7 'NBAUser': '10/m', # 登录用户10/m,NBAUser就是scope定义的值 8 } 9}

setting

1#!/usr/bin/evn python 2#-*-coding:utf-8-*- 3from rest_framework import permissions 4class SVIPPermission(permissions.BasePermission): 5 message = "不是SVIP不给查看" # 当没有权限的时候提示信息 6 7 def has_permission(self, request, view): 8 if request.user.user_type != 3: 9 return False 10 return True 11 12 13class MyPermission(permissions.BasePermission): 14 message = "普通用户不给查看" 15 16 def has_permission(self, request, view): 17 if request.user.user_type == 1: 18 return False 19 return True

app01/utils/permission.py

1#!/usr/bin/evn python 2# -*-coding:utf-8-*- 3from rest_framework import authentication,exceptions 4from app01.models import UserToken 5 6 7class CustomAuthentication(authentication.BaseAuthentication): 8 def authenticate(self, request): 9 token = request.GET.get('token') 10 token_obj = UserToken.objects.filter(token=token).first() 11 if not token_obj: 12 raise exceptions.AuthenticationFailed('验证失败') 13 return (token_obj.user, token_obj.token) 14 15 def authenticate_header(self, request): 16 pass

app01/utils/auth.py

1#!/usr/bin/evn python 2#-*-coding:utf-8-*- 3from app01.models import * 4from rest_framework import serializers 5class AuthorModelSerializers(serializers.ModelSerializer): 6 class Meta: 7 model = Author 8 fields = "__all__" 9 10 11class BookModelSerializers(serializers.ModelSerializer): 12 class Meta: 13 model = Book 14 fields = "__all__" 15 16 17class PublishModelSerializers(serializers.ModelSerializer): 18 class Meta: 19 model = Publish 20 fields = "__all__"

app01/utils/serializers.py

1#!/usr/bin/evn python 2# -*-coding:utf-8-*- 3from rest_framework import throttling 4import time 5 6VISIT_RECORD = {} 7 8 9class VisitThrottle(throttling.BaseThrottle): 10 """60s内只能访问三次""" 11 12 def __init__(self): 13 self.history = None # 初始化访问记录 14 15 def allow_request(self, request, view): 16 remote_addr = self.get_ident(request) 17 ctime = time.time() 18 # 如果当前IP不在访问记录里面,就添加到记录 19 if remote_addr not in VISIT_RECORD: 20 VISIT_RECORD[remote_addr] = [ctime, ] # 键值对的形式保存 21 return True # True表示可以访问 22 # 获取当前ip的历史访问记录 23 history = VISIT_RECORD.get(remote_addr) 24 self.history = history 25 # 如果有历史访问记录,并且最早一次的访问记录离当前时间超过60s,就删除最早的那个访问记录, 26 # 只要为True,就一直循环删除最早的一次访问记录 27 # 结合下面的3次可以知道当我们事件 28 while history and history[-1] < ctime - 60: # 最早的访问一次加60s小于当前时间 29 history.pop() 30 # 如果访问记录不超过三次,就把当前的访问记录插到第一个位置(pop删除最后一个) 31 if len(history) < 3: 32 history.insert(0, ctime) 33 return True 34 else: 35 return False 36 37 def wait(self): 38 '''还需要等多久才能访问''' 39 ctime = time.time() 40 return 60 - (ctime - self.history[-1]) 41 42 43class VisitThrottle2(throttling.SimpleRateThrottle): 44 '''匿名用户60s只能访问4次(根据ip)''' 45 scope = 'NBA' # 这里面的值,自己随便定义,settings里面根据这个值配置Rate 46 47 def get_cache_key(self, request, view): 48 # 通过ip限制节流 49 return self.get_ident(request) 50 51 52class UserThrottle(throttling.SimpleRateThrottle): 53 '''登录用户60s可以访问10次''' 54 scope = 'NBAUser' # 这里面的值,自己随便定义,settings里面根据这个值配置Rate 55 56 def get_cache_key(self, request, view): 57 return request.user.username

app01/utils/throttle.py

1from django.conf.urls import url 2from django.contrib import admin 3from app01 import views 4 5urlpatterns = [ 6 url(r'^admin/', admin.site.urls), 7 url(r'^login/$', views.LoginView.as_view(), name="login"), 8 9 url(r'^books/$', views.BookModelView.as_view({"get": "list", "post": "create"}), name="books"), 10 url(r'^books/(?P<pk>\d+)/$', views.BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}),name="detail_book"), 11 12 url(r'^authors/$', views.AuthorModelView.as_view({"get": "list", "post": "create"}), name="author"), 13 url(r'^authors/(?P<pk>\d+)/$',views.AuthorModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_author"), 14 15 url(r'^publishers/$', views.PublisherModelView.as_view({"get": "list", "post": "create"}), name="publishers"), 16 url(r'^publishers/(?P<pk>\d+)/$',views.PublisherModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}), name="detail_publishers"), 17 18]

url.py

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Django REST

一.什么是RESTfulREST与技术无关,代表的是一种软件架构风格,REST是RepresentationalStateTransfer的简称,中文翻译为“表征状态转移”REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征

Django rest - HelloWorld