django restframework是基于django和restful协议开发的框架html
在restful协议里,一切皆是资源,操做是经过请求方式控制前端
在开始前,你须要对CBV和FBV两种架构模式有个大概的了解,django restframework用的就是CBV架构,这里提供一篇博客供你欣赏CBV
python
安装:pip install djangorestframework
数据库
首先看到下面这段代码django
def post(self, request): print(request.body) print(request.POST)
请求数据通常封装在请求头中,而上面打印的数据,都是通过处理后的数据,那背后是怎么进行封装的呢?json
对于get请求,直接去url后面的数据后端
而对于post请求api
request.body: a=1&b=2 request.POST: if contentType:urlencoded: a=1&b=2 ---> {"a":1,"b":2}
当发urlencoded数据时,两个都能打印数据,可是若是就发json数据,就只有request.body里有了restful
怎么看源码呢?打印下request的类型就能够了,print(type(request)), 查看打印的WSGIRequest就能够了闭包
from django.core.handlers.wsgi import WSGIRequest
去它下面找POST,发现这么一句
POST = property(_get_post, _set_post)
在_get_post方法中,进入这个方法self._load_post_and_files(),在它里面就这么一段代码
elif self.content_type == 'application/x-www-form-urlencoded': #若是urlencoded类型才把self.body赋给了self._post self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict() else: self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()
对于restframework的request,咱们须要了解下在restframework里请求流程,它和django大体相同,由于它的APIView继承是django的View,但在APiView中重写了dispatch方法
看到这段代码
url(r'^publishers/$', views.PublishViewSet.as_view(),name="publish_list"),
执行PublishViewSet就是APIView的as_view方法
class APIView(View):
APIView继承了View,APIView中有as_view方法,因此会执行这个方法,方法中有这么一句代码
view = super(APIView, cls).as_view(**initkwargs)
最终仍是执行了父类里的as_view方法,因此最终执行结果,获得这么这个view函数
def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs)
当请求来时,会执行view函数,把dispatch结果返回,而这里dispatch方法则不是View里的,而是APIView的,由于APIView重写了这个方法,而django restframework的精髓就所有在这里边了
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs #构建一个新的request,把旧的request封装给新的request的_request字段 #执行.data会执行新request类中的data属性,而在self._load_data_and_files()里 ''' if not _hasattr(self, '_data'): self._data, self._files = self._parse() if self._files: self._full_data = self._data.copy() self._full_data.update(self._files) else: self._full_data = self._data ''' #最终返回self._full_data,而上面就是对请求内容进行解析并封装 request = self.initialize_request(request, *args, **kwargs) ''' #post print("request.data", request.data) print("request.data type", type(request.data)) #get print(request._request.GET) print(request.GET) ''' self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed #获取get post等方法后执行,这里用是新的request response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
在dispatch方法,经过请求方式映射,获取到咱们写好的请求方法,并执行
restframework在先后端传输数据时,主要是json数据,过程当中就要须要把其余数据转换成json数据,好比数据库查询全部数据时,是queryset对象,那就要把这对象处理成json数据返回前端
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", on_delete=True) authors=models.ManyToManyField("Author") def __str__(self): return self.title class Publish(models.Model): name=models.CharField(max_length=32) email=models.EmailField() def __str__(self): return self.name class Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() def __str__(self): return self.name
那么这里提供三种序列化的方式:
from django.shortcuts import render, HttpResponse from rest_framework.views import APIView from .models import * # Create your views here. from rest_framework import serializers from rest_framework.response import Response class AuthorSerializers(serializers.Serializer): name = serializers.CharField(max_length=32) age = serializers.IntegerField() class AuthorView(APIView): def get(self, request, *args, **kwargs): authors = Author.objects.all() # 方式1 # data = authors.values("name","age") # from django.forms.models import model_to_dict # data = [] # for obj in authors: # data.append(model_to_dict(obj)) #方式2 # from django.core import serializers # data = serializers.serialize("json", authors) # return HttpResponse(data) #方式3 author_ser = AuthorSerializers(authors, many=True) # return Response(author_ser.data)
上面也只是说了下单表序列化,若是表中涉及到一对多,多对多怎么操做呢?
from django.shortcuts import render, HttpResponse from rest_framework.views import APIView from .models import * # Create your views here. from rest_framework import serializers from rest_framework.response import Response class BookSerializers(serializers.Serializer): title = serializers.CharField(max_length=32) price = serializers.IntegerField() pub_date = serializers.DateField() # 一对多 # publish = serializers.CharField() #不加source时,默认给的是Publish模型定义__str__返回的字段 publish = serializers.CharField(source="publish.name") # 多对多 # authors = serializers.CharField(source="authors.all") #获取是一个queryset对象 字符串 authors = serializers.SerializerMethodField() #经过钩子函数自定制须要的信息 def get_authors(self, obj): temp = [] for author in obj.authors.all(): temp.append({'name':author.name, 'email':author.age}) return temp class BookView(APIView): def get(self, request, *args, **kwargs): books = Book.objects.all() bs = BookSerializers(books, many=True) return Response(bs.data)
固然上面的过程,定义serializers模型,针对表定义每一个字段,有些繁琐,因此序列化模型也有相似于ModelForm用法,不过一对多,多对多都是默认值,取得都是对应对象的id
若是你想定制多对多和一对多,在ModelSerializers重写这类型字段,可是须要注意的是,里面提供的create方法不支持source定制,因此你还须要重写create方法
from django.shortcuts import render, HttpResponse from rest_framework.views import APIView from .models import * # Create your views here. from rest_framework import serializers from rest_framework.response import Response class BookSerializers(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" #source指定字段,不影响get查看序列化,可是影响post建立数据,因此须要你重写create方法 publish = serializers.CharField(source='publish.name') authors = serializers.SerializerMethodField() #经过钩子函数自定制须要的信息 def get_authors(self, obj): temp = [] for author in obj.authors.all(): temp.append({'name':author.name, 'email':author.age}) return temp def create(self, validated_data): # author_list = validated_data.pop("authors") obj = Book.objects.create(**validated_data) # obj.authors.add(*author_list) return obj class BookView(APIView): def get(self, request, *args, **kwargs): books = Book.objects.all() bs = BookSerializers(books, many=True) return Response(bs.data) 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)
序列化超连接:HyperlinkedIdentityField序列化字段,指定三个参数 view_name url别名,lookup_field填入连接里的值对应字段, lookup_url_kwarg url里对应的形参名
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
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"), ]
对于重写create方法情景的总结(Django 1.10.1):
1.序列化类继承serializers.Serializer,须要重写create,update方法,看源码这两个方法只是定义了抛错
class AuthorSerializers(serializers.Serializer): name = serializers.CharField(max_length=32) age = serializers.IntegerField() def create(self, validated_data): obj = Author.objects.create(**validated_data) return obj
2.表对象为单表,序列化类继承serializers.ModelSerializer,不须要重写create,update方法,源码里就在这个类下实现了这两个方法,另外这两个方法是在序列化类下,因此它跟你使用哪一个视图类无关,好比使用generics.ListCreateAPIView
class PublishSerializers(serializers.ModelSerializer): class Meta: model = Publish fields = "__all__"
3.表对象为关联表,序列化类继承serializers.ModelSerializer,不存在定制某个字段,不须要重写create,update方法
class BookSerializers(serializers.ModelSerializer): class Meta: model = Book #book表关联表,有一对多,多对多 fields = "__all__"
4.表对象为关联表,序列化类继承serializers.ModelSerializer,source指定某个字段,提示指定字段要发实例(一对多),存在的疑问就是怎么在前端添加实例?
多对多,若是经过SerializerMethodField生成,序列化时,不提供多对多字段传过来的值,等同这种情景下,多对多只是只读显示,不用来post添加操做,若是要操做把添加对象和多对多添加分红两步操做
class BookSerializers(serializers.ModelSerializer): class Meta: model = Book #book表关联表,有一对多,多对多 fields = "__all__" authors = serializers.SerializerMethodField() def get_authors(self, obj): temp = [] for author in obj.authors.all(): temp.append({'name':author.name, 'email':author.age}) return temp publish = serializers.CharField(source="publish.email") def create(self, validated_data): # authors = validated_data.pop("authors") obj = Book.objects.create(**validated_data) # obj.authors.add(*authors) return obj
下面视图代码,能够说是规规矩矩的作法,每一个视图类下,都写各个请求方法,细心的你确定发现了,每一个视图的同类请求方法实现过程处理序列化模型和查询的表不一样,其余的都同样,代码重复
from rest_framework.views import APIView from rest_framework.response import Response from .models import * from django.shortcuts import HttpResponse from django.core import serializers from rest_framework import serializers class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" #depth=1 class PublshSerializers(serializers.ModelSerializer): class Meta: model=Publish fields="__all__" depth=1 class 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)
restframework已经对咱们使用的这些请求方法,封装到一些类里面,咱们只要继承这些类,并调用特定的方法,把结果返回便可
至因而哪一个序列化模型和哪一个模型的数据,经过静态字段queryset和serializer_class指定
而GenericAPIView继承了APIView,因此在程序启动时,执行的仍是APIView的as_view方法
代码简化以下
from rest_framework import mixins from rest_framework import generics class 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)
咱们大概能够看下这些类封装方法的实现过程,其中源码中的方法能够在GenericAPIView类找到
def list(self, request, *args, **kwargs): #self.get_queryset()获取静态字段指定数据 #filter_queryset支持配置文件筛选数据 queryset = self.filter_queryset(self.get_queryset()) #分页 page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) #get_serializer获取静态字段中指定的序列化类,并实例对象 serializer = self.get_serializer(queryset, many=True) return Response(serializer.data)
固然上面这个实现过程,在定义请求方法,仍是有点重复,rest框架提供了一组已经混合好(minxed-in)的通用视图进一步封装
generics.ListCreateAPIView 查看多条和添加视图
generics.RetrieveUpdateDestroyAPIView 查看单条,修改和删除视图
from rest_framework import mixins from rest_framework import generics class BookViewSet(generics.ListCreateAPIView): queryset = Book.objects.all() serializer_class = BookSerializers class BookDetailViewSet(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookSerializers class PublishViewSet(generics.ListCreateAPIView): queryset = Publish.objects.all() serializer_class = PublshSerializers class PublishDetailViewSet(generics.RetrieveUpdateDestroyAPIView): queryset = Publish.objects.all() serializer_class = PublshSerializers
点进去一个类,发现它其实就是经过多继承组合了第二阶段中的类
class ListCreateAPIView(mixins.ListModelMixin, mixins.CreateModelMixin, GenericAPIView): """ Concrete view for listing a queryset or creating a model instance. """ def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs)
上面的代码已经够简洁了吧,它还能够简洁,你能够观察一下,就是在对全部的书操做和单本书操做,他们使用的数据和序列化模型的同样的,rest框架还作到这方面的简化,他们两本质的区别在于操做全部书是get,post方法,而单本就是get,put,delete方法,实现原理是在url上对请求方法进行映射
url(r'^books/$', views.BookViewSet.as_view({"get":"list","post":"create"}),name="book_list"), url(r'^books/(?P<pk>\d+)$', views.BookViewSet.as_view({ 'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy' }),name="book_detail"),
from rest_framework.viewsets import ModelViewSet
class BookViewSet(viewsets.ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializers
而ModelViewSet实现过程也是多继承
class ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet): pass
那咱们再看下,最后这种方式在请求到来时,是怎么执行的,从多继承中看,找as_view函数
最终仍是会找到ViewSetMixin下的as_view执行,把请求映射关系传给了actions
def as_view(cls, actions=None, **initkwargs):
传入到view函数中,闭包
def view(request, *args, **kwargs): self = cls(**initkwargs) # We also store the mapping of request methods to actions, # so that we can later set the action attribute. # eg. `self.action = 'list'` on an incoming GET request. self.action_map = actions # Bind methods to actions # This is the bit that's different to a standard view for method, action in actions.items(): handler = getattr(self, action) setattr(self, method, handler) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs # And continue as usual return self.dispatch(request, *args, **kwargs)
当请求来时,执行闭包的view函数,在下列这段代码进行请求方法的映射
for method, action in actions.items(): #method = get,post #action=list,create handler = getattr(self, action) #handler==self.list,self.create函数 setattr(self, method, handler) #self.get-->self.list, self.post-->self.create
执行dispatch下这段代码时
if request.method.lower() in self.http_method_names: #get请求时,执行self.list.... handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
认证前提是须要登陆的,因此你还须要写好登陆
models
class User(models.Model): name = models.CharField(max_length=32) pwd = models.CharField(max_length=32)\ def __str__(self): return self.name class Token(models.Model): user = models.OneToOneField("User") token = models.CharField(max_length=128) def __str__(self): return self.token
登陆视图
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 .models import User class LoginView(APIView): def post(self,request): name=request.data.get("name") pwd=request.data.get("pwd") user=User.objects.filter(name=name,pwd=pwd).first() res = {"state_code": 1000, "msg": None} if user: random_str=get_random_str(user.name) token=Token.objects.update_or_create(user=user,defaults={"token":random_str}) res["token"]=random_str else: res["state_code"]=1001 #错误状态码 res["msg"] = "用户名或者密码错误" import json return Response(json.dumps(res,ensure_ascii=False))
首先你必须明确的是,认证,权限,频率那都是请求到来时的操做,大概都能猜到会在分发dispatch下执行,那咱们就找到APIView下dispatch看下,下面这几句代码
你会发现,在进入到认证..这些操做的时候,request已是从新封装后的新的request
request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: #认证 权限 频率都在这里面作了 self.initial(request, *args, **kwargs)
在initial方法下,有这三句代码,分别对应认证,权限,频率组件
self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request)
那咱们就先看认证组件干了些什么事,一旦理解认证组件,其余的两个就好理解了,实现方式是相似的
在perform_authentication认证方法里,就一句代码,你可能惊呼,这啥啊,就这么一句?给人感受是字段啊,但深究下,若是是字段确定实现不了认证功能,你须要大胆猜想是静态方法,是否是?咱们去这个request下找下就知道了,注意:这里request是新的request
def perform_authentication(self, request): """ Perform authentication on the incoming request. Note that if you override this and simply 'pass', then authentication will instead be performed lazily, the first time either `request.user` or `request.auth` is accessed. """ request.user
也就是这个返回的request
request = self.initialize_request(request, *args, **kwargs)
而它返回的就是这个request对象
return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context )
去它下面还真找到user静态方法
@property def user(self): """ Returns the user associated with the current request, as authenticated by the authentication classes provided to the request. """ if not hasattr(self, '_user'): with wrap_attributeerrors(): self._authenticate() return self._user
最后它会执行_authenticate方法
def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ for authenticator in self.authenticators: try: user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() raise if user_auth_tuple is not None: self._authenticator = authenticator self.user, self.auth = user_auth_tuple return self._not_authenticated()
要想搞明白上面代码,就必需要知道self.authenticators是啥?
在__init__方法里,有这么句,而authenticators在实例化对象传下来的
self.authenticators = authenticators or ()
实例化对象时,传入是下面这玩意,那它又干了啥呢?
authenticators=self.get_authenticators(),
下面self就是咱们定义的视图类,它会去咱们定义的类下面找authentication_classes,循环并实例化
def get_authenticators(self): """ Instantiates and returns the list of authenticators that this view can use. """ return [auth() for auth in self.authentication_classes]
authentication_classes又是些啥了?从名字上看,就是认证类,因此这个是由你来定义的,可是若是咱们不定义的呢?
当前类没有,就会找父类去中,一直找到APIView下
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
会去api_settings下取个DEFAULT_AUTHENTICATION_CLASSES默认的认证类
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
None,DEFAULTS对应APISettings下__init__方法的user_settings和defaults
而DEFAULTS则是settings配置文件的变量,并配置这个,默认状况下就是这些认证类
'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication' ),
api_settings.DEFAULT_AUTHENTICATION_CLASSES会去执行APISettings的__getattr__方法
def __getattr__(self, attr): if attr not in self.defaults: raise AttributeError("Invalid API setting: '%s'" % attr) try: # Check if present in user settings #self.user_settings这里调用时静态方法 #attr=DEFAULT_AUTHENTICATION_CLASSES ''' @property def user_settings(self): #user_settings为None,因此这里hasattr(self, '_user_settings')为False if not hasattr(self, '_user_settings'): #尝试去settings配置文件找REST_FRAMEWORK这么个配置 self._user_settings = getattr(settings, 'REST_FRAMEWORK', {}) return self._user_settings ''' #若是在settings找REST_FRAMEWORK配置就返回配置,没找到就是{} #没配置空字典获取DEFAULT_AUTHENTICATION_CLASSES就报错,走异常分支 val = self.user_settings[attr] except KeyError: # Fall back to defaults ''' 去默认配置文件取到这两项 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication' ), ''' val = self.defaults[attr] # Coerce import strings into classes if attr in self.import_strings: val = perform_import(val, attr) # Cache the result self._cached_attrs.add(attr) setattr(self, attr, val) return val
因此咱们能够得出
若是想局部配置认证类,在咱们定义视图类下经过authentication_classes指定(列表)
若是想全局在settings下配置
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.TokenAuth",] }
回到request下user属性方法下的_authenticate方法
def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ for authenticator in self.authenticators: #authenticator 认证类实例 try: #调用认证类实例下的authenticate方法,传入了self,也就是request #因此你认证类要有authenticate方法,返回值为元组 user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() raise if user_auth_tuple is not None: self._authenticator = authenticator #元组内容 用户信息 和 auth信息,咱们用的token self.user, self.auth = user_auth_tuple return
认证类
from rest_framework.authentication import BaseAuthentication from rest_framework.authentication import exceptions class Authentication(BaseAuthentication): def authenticate(self, request): token = request.GET.get("token") token_obj = Token.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed("验证失败") else: return token_obj.user.name, token_obj.token
视图类
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 JsonResponse class 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})
了解认证组件的源码后,下面这两个组件就不带看了,直接看怎么用吧
权限类里重写has_permission方法
from rest_framework.permissions import BasePermission class SVIPPermission(BasePermission): message="SVIP才能访问!" def has_permission(self, request, view): if request.user.user_type==3: return True return False
视图类里经过permission_classe指定权限类
from app01.service.permissions import * class BookViewSet(generics.ListCreateAPIView): permission_classes = [SVIPPermission,] queryset = Book.objects.all() serializer_class = BookSerializers
全局配置
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",] }
依据什么来断定它的访问频率,IP?访问相关的信息你均可以从request.META中获取
频率类里定义allow_request方法
from rest_framework.throttling import BaseThrottle class VisitThrottle(BaseThrottle): ''' 一分钟内访问超过5次 禁用两分钟 ''' VISIT_RECORD = {} def allow_request(self, request, view): remote_addr = request.META.get('REMOTE_ADDR') print(remote_addr) now_time = time.time() if remote_addr not in VisitThrottle.VISIT_RECORD: VisitThrottle.VISIT_RECORD[remote_addr] = { 'start_time' : [now_time,], 'forbid' : False } return True start_time = VisitThrottle.VISIT_RECORD[remote_addr]['start_time'] forbid_state = VisitThrottle.VISIT_RECORD[remote_addr]['forbid'] while start_time and start_time[-1] < now_time - 60 and not forbid_state: start_time.pop() if forbid_state: if now_time - start_time[-1] > 120: print('两分钟过了,解禁') VisitThrottle.VISIT_RECORD[remote_addr] = { 'start_time': [now_time, ], 'forbid': False } print(VisitThrottle.VISIT_RECORD[remote_addr]) return True else: print("两分钟内禁止访问,已通过了%s秒"%(now_time - start_time[-1])) return False else: if len(start_time) < 5: print("访问%s次"%(len(start_time) + 1)) print(start_time) start_time.insert(0, now_time) return True else: print("访问%s次,禁止访问"%len(start_time)) VisitThrottle.VISIT_RECORD[remote_addr] = { 'start_time': [now_time, ], 'forbid': True } return False
视图类中经过throttle_classes指定频率类
from app01.service.throttles import * class BookViewSet(generics.ListCreateAPIView): throttle_classes = [VisitThrottle,] queryset = Book.objects.all() serializer_class = BookSerializers
全局配置
REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",], "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",], "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",] }
内置玩法
class VisitThrottle(SimpleRateThrottle): scope="visit_rate" def get_cache_key(self, request, view): return self.get_ident(request)
settings
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", } }
上面已经对django和restframework的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',] }
响应器主要是Response返回的内容,它会帮你生成展现数据的页面,页面支持你定义好的请求方法,若是你不想看它给页面,也能够经过?format=json查看
分两种状况,一种是原生get,另一种就是钩子映射到list方法,因此你想若是想进行分页,能够这么作
重写get或list方法
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination def get(self,request): book_list=Book.objects.all() pp=PageNumberPagination() 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)
固然上面这个过程是没有给一页显示多少的,并且分页内部没有默认值,它会去settings下取PAGE_SIZE,因此你好配置这个
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.TokenAuth",] "PAGE_SIZE":2 }
固然这个是全局的,若是你想局部,你能够这么干,自定义分页类,下面实例过程就换成你定义这个类
pp=PageNumberPagination()
自定义分页类
class PNPagination(PageNumberPagination): page_size = 1 page_query_param = 'page' #url分页参数名 page_size_query_param = "size" #url每页数参数名 max_page_size = 5 #当?page=1&size=6 size超过5时就不生效
针对钩子映射的,还能够这么作,经过pagination_class指定分页器,固然指定能够不用重写list方法了,由于在list方法的源码里,会自动获取这个指定的分液器
class AuthorModelView(viewsets.ModelViewSet): pagination_class = PNPagination authentication_classes = [TokenAuth,] permission_classes = [] throttle_classes = []# 限制某个IP每分钟访问次数不能超过20次 queryset = Author.objects.all() serializer_class = AuthorModelSerializers
对LimitOffsetPagination偏移分页,自定义类时 default_limit指定限制多少,url里 offset参数指定偏移量