refrem权限组件 和认证组件 解析器,分页,路由

知识点:

  #获取用户的ip地址

#visit_ip=request.META.get('REMOTE_ADDR')


restfromwork 客户端发送post,等ajax请求的时候不用发送csrf信息
是因为在源码中,对as_view()方法进行了设置,使他不进行csrf验证
 

认证与权限组件

在使用这些组件之前要先写好模板

认证的功能:
    将请求中的数据进行验证然后查看当前用户的信息
    并经信息返回,可以在视图类中调用,也可以在权限组件中调用认证之后的信息
  如用户登陆之后找到当前的用户信息
  认证类中就可以返回两个信息回来
  调用方式 request.user request.auth 这两种方式都可以调用

 

 

class User(models.Model):

    user=models.CharField(max_length=32)
    pwd=models.CharField(max_length=32)
    #此字段时用于权限组件中来判断用户权限级别
    #从而确定用户拥有那些权限
    user_type=models.IntegerField(choices=((1,"普通用户"),(2,"VIP"),(3,"SVIP")),default=1)

#此组件是用于判断认证组件的,并一对一关联到用户表
#当用户登录时登录试图会给url加上一个token 字段
#通过取出这个值判断是否已登陆   详见认证组件的原理和登录视图
class UserToken(models.Model):

    user=models.OneToOneField("User")
    token=models.CharField(max_length=128)

 

认证组件

源码解析>>点我

局部视图认证

这里注意认证组件执行的位置是在APIView执行到dispatch()的时候执行的

中间件的认证比dispatch() 早

 

在app01.service.auth.py:

#encoding=utf-8
from rest_framework import exceptions
#引入报错组件,当出现错误时报这个错
from ..models import  *
from rest_framework.authentication import BaseAuthentication

#这里继承了BaseAuthentication   它里边有两个方法
#  def authenticate  和 def authenticate_header
# 这里因为我只用到了一种authenticate方法以另外一个我不用写,
#直接继承即可
class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        #在get请求头中取出‘token’ 因为此时的request已经被重新封装了
        #原来的request被封装在request._request 所以用他来调用GET中的内容
        token=request._request.GET.get('token')
        user_token_obj=UserToken.objects.filter(token=token).first()
        if user_token_obj:
            #当验证成功时,返回两个数据
            return  user_token_obj.user,token
        else:
            #失败是抛出错误
            raise exceptions.AuthenticationFailed('验证失败')

 

在views.py:

定义一个登录视图类

#定义一个生成随机字符串的函数,将他的值付给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()
#定义一个登录视图
class LoginView(APIView):
    authentication_classes = []
    def post(self,request,*args,**kwargs):
        print('jinlaile ')
        user=request.data.get("user")
        pwd=request.data.get("pwd")
        #验证用户是否登录成功
        user=User.objects.filter(user=user,pwd=pwd).first()
        res={'state_code':200,'msg':None}
        if user:
            random_str=get_random_str(user.user)

            #创建或者更新一条数据在此例中,当此user已经有这个
            #当用户登录成功的时候,给UserToken中去添加或者更新数据
            #此表中的数据有一个一对一的外键用于关联User表,
            #另外一个token字段为随机字符串,  当浏览器浏览其他视图时
            #带有的token值与  Token表单中的某一条一致,代表用于已经登录
            #此属性会在自定义的验证组件中使用
            user_token_obj=UserToken.objects.update_or_create(user=user,defaults={"token":random_str})
            res["msg"]="登陆成功"
        else:
            res["msg"] ="登陆失败"
        return JsonResponse(res)

 

定义一个给图书模板序列化的类

#定义一个给图书模板序列化的类
class BookSerializersModel(serializers.ModelSerializer):
    #显示出来外键关联的内容的查看连接
    publish=serializers.HyperlinkedIdentityField(
        view_name='publish_detail',
        lookup_field='publish_id',
        lookup_url_kwarg="pk"
                                                    )
    class Meta:
        model=Book
        fields='__all__'
    #自定义个别字段的显示内容
    authors = serializers.SerializerMethodField()
    def get_authors(self, obj):
        temp = []
        for authors in obj.authors.all():
            temp.append({'name': authors.name})
        return temp

定义一个BookView类

from api.service.auth import *
class BookView(ModelViewSet):
    #给这个视图单独定义验证组件,authentication_classes
    #在APIView中会直接调用用户自己定义的authentication_classes
    authentication_classes = [MyAuthentication]
  #这里的authentication_classes的名字是固定的
permission_classes
=[] #这两个内容必须要写上,在源码中会使用这些内容 #传入queryset queryset = Book.objects.all() #传入一个自定义序列化图书模板的类,在源码中会调用self.serializer 会调用这个 serializer_class=BookSerializersModel

 

 

全局视图认证组件

settings.py配置如下:

1
2
3
REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",]
}
 
#列表中写的内容是自定义验证组件的路径

权限组件

局部视图权限

在app01.service.permissions.py中:

from rest_framework.permissions import BasePermission
class SVIPPermisson(BasePermission):
    def has_permission(self,request,view):
        #因为self.user, self.auth = user_auth_tuple,有这两个
        #这里的self就是request所以这里可以调用request.user得到auth验证中返回
        #的第一个  #当验证成功时,返回两个数据
        # return  user_token_obj.user,user_token_obj
        #接收第一个user_token_obj.user
        if request.user.user_type==3:
            return True
        else:
            return False

在views.py:

from api.service.auth import *
from api.service.permission import *
class BookView(ModelViewSet):
    #给这个视图单独定义验证组件,authentication_classes
    #在APIView中会直接调用用户自己定义的authentication_classes
    authentication_classes = [MyAuthentication]
    #权限组件是在认证组件的基础上的 因为权限组件
    #用到了认证组件中的内容request.user
    permission_classes=[SVIPPermisson]
    #这两个内容必须要写上,在源码中会使用这些内容
    #传入queryset
    queryset = Book.objects.all()
    #传入一个自定义序列化图书模板的类
    serializer_class=BookSerializersModel

 

全局视图权限

settings.py配置如下:

1
2
3
4
REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",]
}

throttle(访问频率)组件

局部视图throttle

在app01.service.throttles.py中:

from rest_framework.throttling import BaseThrottle,SimpleRateThrottle

import time
#在函数外部定义一个字典,因为在内部定义每一次都会从新更新字典,将其值为空
VISITED_RECORD={}

class VisitThrottle(BaseThrottle):
    def __init__(self):
        self.history=None

    def allow_request(self,request,view):
        print("ident",self.get_ident(request))
     #获取用户的ip地址
#visit_ip=request.META.get('REMOTE_ADDR') visit_ip=self.get_ident(request) print(visit_ip) ctime=time.time() #第一次访问请求 if visit_ip not in VISITED_RECORD: VISITED_RECORD[visit_ip]=[ctime] return True # self.history:当前请求IP的记录列表 self.history = VISITED_RECORD[visit_ip] print(self.history) # 第2,3次访问请求 if len(VISITED_RECORD[visit_ip])<3: VISITED_RECORD[visit_ip].insert(0,ctime) return True if ctime-VISITED_RECORD[visit_ip][-1]>60: VISITED_RECORD[visit_ip].pop() VISITED_RECORD[visit_ip].insert(0,ctime) print("ok") return True return False #当returnFalse的时候,会调用wait函数 def wait(self): import time ctime = time.time() return 60 - (ctime - self.history[-1])

 

在views.py中:

复制代码
 
 
from app01.service.throttles import *

class BookViewSet(generics.ListCreateAPIView):
    throttle_classes = [VisitThrottle,]
    queryset = Book.objects.all()
    serializer_class = BookSerializers
复制代码

全局视图throttle

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
   #前边的名字是固定的 后边的内容是自定义的类的路径 "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",] }

内置throttle类

在app01.service.throttles.py修改为:

复制代码
 
 
from rest_framework.throttling import SimpleRateThrottle
class VisitThrottle(SimpleRateThrottle):

    scope="visit_rate"
    def get_cache_key(self, request, view):

        return self.get_ident(request)
复制代码

settings.py设置:

复制代码
REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
    "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",],
    "DEFAULT_THROTTLE_RATES":{
        "visit_rate":"5/m", #这里注意 m=60 h=60*60 以此类推  前后两个值都可以进行设置
    }
}
复制代码

解析器

request类

django的request类和rest-framework的request类的源码解析   所有请求过来的数据都放在了request.data中 

 

   所以这里注意  当传送的数据是j'son 的类型时不能够使用request.data.get_list('k1')的方法去得到数据,

因为只有数据时QueryDict的类型的数据才有此方法

 在postman中提交json数据的提交方式

 在postman中提交urlencod数据的提交方式

 

 因为没有给他设置解析器,然后使用默认的解析器,默认解析器有着三种

 自定义解析器

 如何改变返回给网页中渲染出的页面

 

 

 

局部视图

复制代码
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',]
}
复制代码
 
 

 

路由

 没有引入路由的时候定义url 的写法

 注意 :继承了GenericViewSet,或者Modelviewset  的视图类  在写url的时候,都要在url 中的  as_view()中加入对应关系

都要定义一个queryset,否则就会报错,找不到这个内容



1. django rest framework
        - 路由:
            API:
                列表:
                    ^/api/(?P<version>[v1|v2]+)/course/$                            CourseView.as_view()
                    示例: /api/v1/course/
                    
                    ^/api/(?P<version>[v1|v2]+)/course\.(?P<format>\w+)$            CourseView.as_view()
                    示例: /api/v1/course.json   在后端配置好对应返回的数据就可以,.json就在后端定义给用户返回json形式的文件 
                    
                    
                详细:
                    ^/api/(?P<version>[v1|v2]+)/course/(?P<pk>\d+)/$                    CourseView.as_view()
                    示例: /api/v1/course/1/      v1表示版本  依据版本来访问  这个一般网站都有的
                    
                    
                    ^/api/(?P<version>[v1|v2]+)/course/(?P<pk>\d+)\.(?P<format>\w+)$    CourseView.as_view()
                    示例: /api/v1/course/1.json 
        - 视图:
            第一类:   此类视图是继承了view  需要用户自己去定义视图类中的  get  post delete  updata等方法
         但是需要用户
        至少写两个视图类,一个来处理不带pk 的 如 list post
 一个来处理带pk的 如显示单个数据, 删除 修改

          View APIView(View) GenericAPIView(views.APIView) 注意:
             路由:
^/api/(?P<version>[v1|v2]+)/course/$ CourseView.as_view()自动触发get/post/delete等方法 注意as_view()中没有数据    第二类: 他继承了mixin中的类 这些类中定义了对应的方法 如DestroyModelMixin中定了distroy方法
      需要把delete:dstroy对应关系写到as_view中去 GenericViewSet(ViewSetMixin, generics.GenericAPIView) ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet ) 注意: 路由:
^/api/(?P<version>[v1|v2]+)/course/$ CourseView.as_view({'get':'list','post':"creat",}), 自动触发get/post/delete等方法

     在继承了Modelviewset类的试图函数中的url 要写成如下形式 以区分有没有PK键

            我们的视图到底应该继承哪个类?
                a. 简单的接口,返回的数据比较简单没有跨表查询什么的就,继承 ModelViewSet  或者mixin中的类
                    PS: 继承 class XXView(mixins.ListModelMixin,GenericViewSet)
                b. 复杂接口有外键,部分内容不现实,部分内容显示什么,这种就要自己去写get list等方法- APIView(View)
                    - GenericAPIView(views.APIView)
                

 

 

 

 

urlpatterns = [
      url(r'^admin/', admin.site.urls),
    url(r"books\.(?P<format>\w+)$",views.BookViewSet.as_view({"get":"list","post":"create"}),name="book_list"),
    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","delete":"destroy","put":"update"}),name="book_detail"),
     url(r"books/(?P<pk>\d+)\.(?P<format>\w+)$",views.BookViewSet.as_view({"get":"retrieve","delete":"destroy","put":"update"}),name="book_detail"),
]

 

 

路由模块的用法

from django.conf.urls import url,include
from django.contrib import admin

from api import views
#引入路由模块
from rest_framework import routers
#实例话一个路由对象
router = routers.DefaultRouter()
#将试图类注册到路由中
router.register(r'books', views.BookViewSet)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    #使用路由
    url(r'^', include(router.urls)),
]

 

 

分页

 

 一 直接使用原生分页类


 

 注意使用原生类的时候一定要去setting中设置PAGE_SIZE

 

简单分页

2  自定义一个分页类

复制代码
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination

class PNPagination(PageNumberPagination):
        page_size = 1  #限定每页显示多少条数据
        page_query_param = 'page'  
  #这个配置决定在这儿输入“内容”=“页码” 跳转到相应的页面

 

        page_size_query_param = "size"
        max_page_size = 5   #限制每页最多能显示多少条数据

class BookViewSet(viewsets.ModelViewSet):

    queryset = Book.objects.all()
    serializer_class = BookSerializers
    def list(self,request,*args,**kwargs):

        book_list=Book.objects.all()
        pp=LimitOffsetPagination()
        pager_books=pp.paginate_queryset(queryset=book_list,request=request,view=self)
        print(pager_books)
        bs=BookSerializers(pager_books,many=True)

        #return Response(bs.data)
        return pp.get_paginated_response(bs.data)
复制代码

 自定义的分页器

 

 

 

偏移分页

 

定义方式
from
rest_framework.pagination import LimitOffsetPagination class MyPageNumberPagination(LimitOffsetPagination):pass

页面中这样输入就能得到想要的数据

加密页码形式:
from rest_framework.pagination import CursorPagination
class MyPageNumberPagination(CursorPagination):
    cursor_query_param="page"
    page_size=2
    ordering="id"

页码的显示形式:    所以不能用户自己输入页面,只能通过点击链接跳转页码

 






posted on 2018-04-10 20:47  王大拿  阅读(344)  评论(0)    收藏  举报

导航