澳门新萄京app:framework框架的主导组件

作者: 澳门新萄京app  发布:2019-10-20

1.认证

  流程:请求到达REST framework的时候,会对request进行二次封装,在封装的过程中会对客户端发送过来的request封装进认证,选择,解析等功能。request方法封装完成之后,执行initial方法时,又会再次对客户端的请求执行认证操作,确保请求的合法性

  生命周期:

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

澳门新萄京app 1

 自定义认证:

models.py(创建完后自行添加几条数据)

from django.db import models

# 用户信息
class UserInfo(models.Model):
 username = models.CharField(max_length=32, unique=True)
 password = models.CharField(max_length=32)
 # 小整数字段
 type = models.SmallIntegerField(
  choices=((1,"普通用户"),(2,"VIP用户")),
  default=1
 )


# 用户认证
class Token(models.Model):
 token = models.CharField(max_length=32)
 user = models.OneToOneField(to="UserInfo")


# 评论表
class Comment(models.Model):
 content = models.CharField(max_length=128)
 user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE, default=1) 

url.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'login/$', views.LoginView.as_view()),
]


from rest_framework.routers import DefaultRouter
router = DefaultRouter()
# 注册路由,表示路径comment对应视图函数CommentViewSet
router.register(r'comment', views.CommentViewSet)
urlpatterns += router.urls  

views.py

import time
import hashlib
from app01 import models
from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import app01_serializers     # 自定义的序列化
from rest_framework.viewsets import ModelViewSet

# 生成token函数
def get_token_code(username):
 """
 根据用户名和时间戳生成用户登陆成功的随机字符串
 :param username:字符串格式的用户名
 :return:字符串格式的token
 """
 timestamp = str(time.time())
 m = hashlib.md5(bytes(username,encoding="utf-8"))
 m.update(bytes(timestamp,encoding="utf-8"))
 return m.hexdigest()


class LoginView(APIView):
 """
 1. 接收用户发过来(POST)的用户名和密码数据
 2. 校验用户名密码是否正确
  - 成功就返回登陆成功(发Token)
  - 失败就返回错误提示
 """
 def post(self,request):
  res = {"code":0}
  username = request.data.get("username")
  password = request.data.get("password")
  user_obj = models.UserInfo.objects.filter(
   username=username,
   password=password,
  ).first()
  if user_obj:
   token = get_token_code(username)
   # 保存token
   # 如果有记录就更新defaults里传的参数, 没有记录就用defaults里传的参数创建一条数据
   models.Token.objects.update_or_create(defaults={"token": token}, user=user_obj)
   # 将token返回给用户
   res["token"] = token
  else:
   res["token"] = 1
   res["error"] = "用户名或密码错误"
  return  Response(res)


# 对文章进行增删改查操作
class CommentViewSet(ModelViewSet):
 queryset = models.Comment.objects.all()
 serializer_class = app01_serializers.CommentSerializer

app01_serializers.py(app下创建的py文件)

from app01.models import Comment
from rest_framework import serializers


class CommentSerializer(serializers.ModelSerializer):
 class Meta:
  model = Comment
  fields = "__all__"

 通过在postman上模拟post发送请求登录,如果用户名和密码正确的话,会生成token值,下次该用户再登录时,token的值就会更新

澳门新萄京app 2

当用户名或密码错误时,抛出异常

澳门新萄京app 3

上面看是没有什么问题,但是任何用户都能访问评论,如何只让登陆用户查看信息呢?这里我们就需要添加一个认证类

在app01(应用名)目录下创建目录utils,在此目录下创建auth.py,用于放置自定义的认证类

auth.py

from app01 import models
from rest_framework.authentication import BaseAuthentication
# 导入处理REST框架引发的异常的模块
from rest_framework.exceptions import AuthenticationFailed


class MyAuth(BaseAuthentication):
 # 重写BaseAuthentication里的方法
 def authenticate(self, request):
  if request.method in ["POST","PUT",]:
   # 取token值
   token = request.data.get("token")
   # 去数据库查询有没有这个token
   token_obj = models.Token.objects.filter(token=token).first()
   if token_obj:
    # token_obj有2个属性,详见models.py中的Token。
    # return后面的代码,相当于分别赋值。例如a=1,b=2等同于a,b=1,2
    # return多个值,返回一个元组
    # 在rest framework内部会将这两个字段赋值给request,以供后续操作使用
    return token_obj.user, token
   else:
    raise AuthenticationFailed("无效的token")
  else:
   return None, None  

  views.py下的CommentViewSet类:

# app01.utils.auth表示app01目录下的utils下的auth.py
from app01.utils.auth import MyAuth

class CommentViewSet(ModelViewSet):
 queryset = models.Comment.objects.all()
 serializer_class = app01_serializers.CommentSerializer
 authentication_classes = [MyAuth, ]  # 局部使用认证方法MyAuth

 验证:发送一个空的post请求或者错误的post请求

澳门新萄京app 4

发送正确的post请求:

澳门新萄京app 5

澳门新萄京app 6

## 以上只是做了一个局部的认证,对于全局认证:

需要在setting中配置:

REST_FRAMEWORK = {
    # 表示app01-->utils下的auth.py里面的MyAuth类
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.utils.auth.MyAuth", ]
}

 这时候CommentViewSet中的authentication_classes就可以注释掉了,效果也是一样的  

序列化

2.权限

  自定义我们的权限,如只有让VIP用户才能看的内容

 注意:这里我把上面的认证权限设立在了全局,在settings里面设置全局认证,所有业务都需要经过认证,只有这样配置执行下面代码时你的permissions.py下的request才不会始终返回AnonymousUser(匿名用户)

创建一个序列化类

2.1 验证一

permissions.py(在utils文件下下创建方认证文件)

from rest_framework.permissions import BasePermission

class MyPermission(BasePermission):
 # 重写BasePermission里的方法,点击源码,里面只给了框架
 def has_permission(self, request, view):
  """
  判断该用户有没有权限
  """
  print('我要进行自定义的权限判断啦....')
  print(request)
  print(request.user)
  return True

 views.py(其他的不变动)

from app01.utils.permissions import MyPermission
class CommentViewSet(ModelViewSet):
 queryset = models.Comment.objects.all()
 serializer_class = app01_serializers.CommentSerializer
 permission_classes = [MyPermission, ]

发送get请求,request.user 打印值为None

发送post请求,并携带之前用户登录加载的token值,request.user 打印值为UserInfo object

澳门新萄京app 7

简单使用

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

models部分:

澳门新萄京app 8

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    def __str__:        return self.titleclass Publish(models.Model):    name=models.CharField(max_length=32)    email=models.EmailField()    def __str__:        return self.nameclass Author(models.Model):    name=models.CharField(max_length=32)    age=models.IntegerField()    def __str__:        return self.name

澳门新萄京app 9

views部分:

澳门新萄京app 10

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.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:        temp=[]        for author in obj.authors.all():            temp.append(author.name)        return tempclass BookViewSet:    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        # print        # return HttpResponse        # 序列化方式2:        # data=serializers.serialize("json",book_list)        # return HttpResponse        # 序列化方式3:        bs=BookSerializers(book_list,many=True)        return Response

澳门新萄京app 11

2.2验证二(验证request到底是啥玩意)

修改permission.py

class MyPermission(BasePermission):
 # 重写BasePermission里的方法,点击源码,里面只给了框架
 def has_permission(self, request, view):
  print('我要进行自定义的权限判断啦....')
  print(request)
  print(request.user)
  print(request.user.username)
  print(request.user.type)
  return True

 再次发送相同的post请求,request.user.username打印值为携带当前token值的用户名

            request.user.type打印值为携带当前token值的用户的type

   Request有个user方法,加 @property 表示调用user方法的时候不需要加括号“user()”,可以直接调用:request.user

    在rest framework内部会将这两个字段赋值给request,以供后续操作使用

    return (token_obj.user,token_obj) 上面的return的值,来源于app01utilsauth.py里面的MyAuth类中的return token_obj.user, token

    通过认证之后,它会request进行再次封装,所以调用request.user时,得到了一个对象

    这个对象就是执行models.Token.objects.filter(token=token).first()的结果

    如果ORM没有查询出结果,它就一个匿名用户(AnonymousUser)!

ModelSerializer

class BookSerializers(serializers.ModelSerializer):      class Meta:          model=Book          fields="__all__"          depth=1

2.3 验证三(只有VIP用户登录才能操作内容)

   修改permission.py,仅限VIP用户登录操作文章内容

permission.py

from rest_framework.permissions import BasePermission

class MyPermission(BasePermission):
 # 重写BasePermission里的方法,点击源码,里面只给了框架
 # 自定义输出语句
 message = '快滚,没有权限!'
 def has_permission(self, request, view):
  if request.method in ['POST', 'PUT', 'DELETE']:
   print(request.user.username)
   print(request.user.type)
   if request.user.type == 2:  # 是VIP用户
    return True
   else:
    return False
  else:
   return True

效果(VIP客户):

澳门新萄京app 12

comment内容:

澳门新萄京app 13

非VIP客户:

澳门新萄京app 14

提交post请求

澳门新萄京app 15

  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        else:            return HttpResponse(bs.errors)

澳门新萄京app 16

2.4 验证四(使用普通用户的token发送delete类型的请求)

  重写BasePermission里的has_object_permission方法

from rest_framework.permissions import BasePermission

class MyPermission(BasePermission):
 # 重写BasePermission里的方法,点击源码,里面只给了框架
 # 自定义输出语句
 message = '快滚,没有权限!'
 def has_permission(self, request, view):
  print("源码里面这个方法下啥也没有")
  return True


 # 源码中给的另一方法
 def has_object_permission(self, request, view, obj):
  """
  判断当前评论用户的作者是不是你当前的用户
  只有是本人才能修改或者删除自己的评论
  """
  print('这是在自定义权限类中的has_object_permission')
  print(obj.id)
  if request.method in ['PUT', 'DELETE']:
   if obj.user == request.user:
    # 当前要删除的评论的作者就是当前登陆的用户
    return True
   else:
    return False
  else:
   return True  

 使用普通用户的token发送delete类型的请求:

澳门新萄京app 17

使用VIP用户的token发送delete类型的请求,返回结果为空,删除成功

全局设置权限:

在setting中配置:

REST_FRAMEWORK = {
    # 表示app01-->utils下的auth.py里面的MyPermission类
  "DEFAULT_PERMISSION_CLASSES": ["app01.utils.permission.MyPermission", ]
}

 再注释掉局部设置 

重写save中的create方法

澳门新萄京app 18

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          return obj

澳门新萄京app 19

3.限制(节流)

 对IP做限制,用户固定时间内只能访问固定次数

在app01utils下面创建throttle.py

 throttle.py

from rest_framework.throttling import BaseThrottle
import time

D = {}  # {'127.0.0.1': [1533302442, 1533302439,...]}

class MyThrottle(BaseThrottle):
 # 点进源码直接看当中携带的参数
 def allow_request(self, request, view):
  """
        Return `True` if the request should be allowed, `False` otherwise.
        """
  # 访问当前IP
  ip = request.META.get('REMOTE_ADDR')
  print(ip)
  now = time.time()
  if ip not in D:
   D[ip] = []  # 初始化一个空的访问历史列表

  history = D[ip]
  # 当历史列表中有元素,并且当前时间戳减去最后一个元素的时间戳大于10
  while history and (now - history[-1]) > 10:
   history.pop()
  # 判断最近10秒内的访问次数是否超过了阈值(3次)
  if len(history) >= 3:
   return False
  else:
   # 把这一次的访问时间加到访问历史列表的第一位
   D[ip].insert(0,now)
   return True

 views.py

from app01.utils.throttle import MyThrottle
class CommentViewSet(ModelViewSet):
 queryset = models.Comment.objects.all()
 serializer_class = app01_serializers.CommentSerializer
 throttle_classes = [MyThrottle, ]   # 局部配置

 或全局配置,在setting中加入:

REST_FRAMEWORK = {
     "DEFAULT_THROTTLE_CLASSES": ["app01.utils.throttle.MyThrottle", ]
}  

 效果:使用postman发送GET请求,十秒内点击数大于三次就会提示你:

澳门新萄京app 20

等待10秒后又能使用

单条数据的get和put请求

澳门新萄京app 21

class BookDetailViewSet:    def get(self,request,pk):        book_obj=Book.objects.filter.first()        bs=BookSerializers        return Response    def put(self,request,pk):        book_obj=Book.objects.filter.first()        bs=BookSerializers(book_obj,data=request.data)        if bs.is_valid():            bs.save()            return Response        else:            return HttpResponse(bs.errors)

澳门新萄京app 22

3.1使用内置的SimpleRateThrottle类

 throttle.py

from rest_framework.throttling import SimpleRateThrottle
class MyThrottle(SimpleRateThrottle):
    scope = "rate"  # rate是名字,可以随便定义!
    def get_cache_key(self, request, view):
        return self.get_ident(request)  # 远程IP地址

 修改全局配置setting:

REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": ["app01.utils.throttle.MyThrottle", ],
"DEFAULT_THROTTLE_RATES": {
 # rate对应的是throttle.py里面MyThrottle定义的scope属性的值
 "rate": "3/m",  # 1分钟3次
  }
}

效果:

澳门新萄京app 23

更过关于节流的代码原理性操作猛戳这里

超链接API:Hyperlinked

澳门新萄京app 24

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

澳门新萄京app 25

urls部分:

123456 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"),]

视图三部曲

使用混合

上一节的视图部分:

澳门新萄京app 26

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:    def get(self,request,*args,**kwargs):        book_list=Book.objects.all()        bs=BookSerializers(book_list,many=True,context={'request': request})        return Response    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        else:            return HttpResponse(bs.errors)class BookDetailViewSet:    def get(self,request,pk):        book_obj=Book.objects.filter.first()        bs=BookSerializers(book_obj,context={'request': request})        return Response    def put(self,request,pk):        book_obj=Book.objects.filter.first()        bs=BookSerializers(book_obj,data=request.data,context={'request': request})        if bs.is_valid():            bs.save()            return Response        else:            return HttpResponse(bs.errors)class PublishViewSet:    def get(self,request,*args,**kwargs):        publish_list=Publish.objects.all()        bs=PublshSerializers(publish_list,many=True,context={'request': request})        return Response    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        else:            return HttpResponse(bs.errors)class PublishDetailViewSet:    def get(self,request,pk):        publish_obj=Publish.objects.filter.first()        bs=PublshSerializers(publish_obj,context={'request': request})        return Response    def put(self,request,pk):        publish_obj=Publish.objects.filter.first()        bs=PublshSerializers(publish_obj,data=request.data,context={'request': request})        if bs.is_valid():            bs.save()            return Response        else:            return HttpResponse(bs.errors)

澳门新萄京app 27

mixin类编写视图

澳门新萄京app 28

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)

澳门新萄京app 29

使用通用的基于类的视图

通过使用mixin类,我们使用更少的代码重写了这些视图,但我们还可以再进一步。REST框架提供了一组已经混合好的通用视图,我们可以使用它来简化我们的views.py模块。

澳门新萄京app 30

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

澳门新萄京app 31

viewsets.ModelViewSet

urls.py:

澳门新萄京app 32

    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"),

澳门新萄京app 33

views.py:

class BookViewSet(viewsets.ModelViewSet):    queryset = Book.objects.all()    serializer_class = BookSerializers

认证与权限组件

认证组件

局部视图认证

在app01.service.auth.py:

澳门新萄京app 34

class Authentication(BaseAuthentication):    def authenticate(self,request):        token=request._request.GET.get        token_obj=UserToken.objects.filter(token=token).first()        if not token_obj:            raise exceptions.AuthenticationFailed        return (token_obj.user,token_obj)

澳门新萄京app 35

在views.py:

澳门新萄京app 36

def get_random_str:    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:    authentication_classes = [Authentication,]    def post(self,request,*args,**kwargs):        res={"code":1000,"msg":None}        try:            user=request._request.POST.get            pwd=request._request.POST.get            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                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})

澳门新萄京app 37

本文由澳门新萄京app发布于澳门新萄京app,转载请注明出处:澳门新萄京app:framework框架的主导组件

关键词: