drf从入门到精通---day07(认证组件,权限组件,频率组件,过滤与排序,分页)

上节课回顾

# 1     两个视图基类
	APIView
	GenericAPIView:跟数据库打交道,而且需要序列化反序列化,可以使用它
------------------------------------

# 2    5 个视图扩展类 + GenericAPIView   = 视图类
-list
-retrieve
-destroy
-create
-update

-正常需要写两个视图类,实现5个接口
	-BookView:list,create
	-BookDetailView:retrieve,destroy,update
	-5个接口只想写新增和更新----》两个视图类
	-如果配合自动生成路由:ViewSetMixin+list+update+GenericAPIView  可以写到一个视图类中
	-5个接口:自动生成路由+5个视图扩展类+GenericAPIView---》一个视图类即可
------------------------------------
# 3   9个视图子类   视图类
	-5个,两两组合,三个组合

------------------------------------
# 4  视图集
	-ModelViewSet
	-ReadOnlyModelViewSet

	-ViewSetMixin: 不是视图类,配合视图类一起用,重写了as_view---》路由写法变了--》本质就是通过传的actions做映射{'get':"xxx"}  像地址发送get请求,就会执行视图类里面的XXX函数
	两张方式:
		-自己写路由,手动传actions的方式( as_view({'get':"XXX"}) )
		-自动生成路由的方式


	-继承APIView+自动生成路由----》ViewSet
	-继承GenericAPIView+自动生成路由----》GenericViewSet


	-引申出来:后期在视图类中的方法,可以任意命名,只要做好映射,或自动生成即可

------------------------------------
# 5 路由组件
-三种写法
    -传统写法:不继承ViewSetMixin及其子类的
    -映射写法:传actions
    -自动生成写法:两个类,加入到路由中:两种(include,列表直接加)


-action装饰器
-自动生成路由时,做自动生成,装饰在视图类的方法上
    @action(methods=['POST','GET'],detail=False)

------------------------------------
# 6 登录接口---》自动生成路由,注意在继承的时候ViewSetMixin一定要放前面,APIView放后面

	-ViewSetMixin+APIView   login  ---》使用action装饰器装饰---》post请求
	-取出前端传入的数据(三种格式都可以)---》request.data
	-写逻辑判断是否登录成功----》UserToken存储登录状态
		-如果存在修改,不存在就更新
	-返回给前端
------------------------------------

.
.
.
.

今日内容

1 认证组件

# 以后,有的接口需要登录后才能访问,有的接口,不登录就能访问
	-登录认证的限制

# 写一个登录接口,返回token,以后只要带着token过来,就是登录了,不带,就没有登录

.
.

1.1 认证组件使用步骤

需求:
# 查询所有不需要登录就能访问
# 查询单个,需要登录才能访问
-----------------------------------
# 代码实现:

# 路由代码
router.register('books', views.BookView, 'books')
router.register('books', views.BookDetailView, 'books')
------------------------------------
# 模型表里面代码

# 查询所有
class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

# 查询单个
class BookDetailView(ViewSetMixin, RetrieveAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    # authentication_classes = [LoginAuth]  # 需要写一个认证类,需要咱们写

------------------------------------
authenticate.py 里面代码

from app01.models import UserToken
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed

class LoginAuth(BaseAuthentication):

    # 重写父类里面的authenticate方法
    def authenticate(self, request):
        # 实现用户的认证,如果是登录的,往后走,不是抛异常
        # 请求中是否携带token,判断是否登录,token放在地址栏中或请求头中
        token = request.query_params.get('token', None)
        if token:
            # 前端传入了token,去表中查询如果能查到,说明登录了,
            # 固定返回两个值,当前登录用户对象 与 token
            ut_obj = UserToken.objects.filter(token=token).first()
            if ut_obj:
                return ut_obj.user, token
            else:
                # 没有登录,抛认证失败的异常
                raise AuthenticationFailed('token认证失败')
        else:
            raise AuthenticationFailed('token没传')

-----------------------------------
-----------------------------------
总结:
# 1 写一个认证类,继承BaseAuthentication
# 2 重写authenticate方法,在该方法在中实现登录认证:token在哪带的?
# 3 如果认证成功,返回两个值【返回None或两个值】
# 4 认证不通过,抛异常AuthenticationFailed
# 5 局部使用和全局使用

-局部:只在某个视图类中使用( 当前视图类管理的所有接口都会在运行前进行认证的校验 )
class BookDetailView(ViewSetMixin, RetrieveAPIView):
    authentication_classes = [LoginAuth]
-----------------------------------

-全局:全局所有接口都生效(但是登录接口不能要,其他接口需要登录后才能访问,登录接口不可能还需要登录后才能访问,那就没法登录了!!!)
REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES':['app01.authenticate.LoginAuth']}

-----------------------------------

-所以要局部禁用 把authentication_classes设为空列表 取消掉全局的登录认证:
class UserView(ViewSet):
    authentication_classes = []

    @action(methods=['POST'], detail=False)
    def login(self, request):
        pass

.
.

# 注意在drf的配置文件里面,有个坑
	-不要在配置文件中乱导入不使用的东西,否则会报错

.
.
.
.

2 权限组件

# 我们发现即便登录成功了,有些接口,还是不能访问,因为没有权限

# 登录后,有的接口有权限访问,有的没有权限访问

# 注意权限组件是在认证组件认证后,才执行的!!!

.

2.1 权限的使用

# 需求
# 查询单个和查询所有,都要登录才能访问----》全局认证
	-查询单个接口,需要超级管理员才能访问
	-查询所有接口,所有登录用户都能访问

# 权限应该是一个字段,需要在User表中,加入user_type字段
----------------------------------
# 代码实现:

# 视图里面代码
class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    user_type = models.IntegerField(choices=((1, '超级管理员'), (2, '普通用户'), (3, '弱智用户')), default=2)


class UserToken(models.Model):
    token = models.CharField(max_length=32)
    user = models.OneToOneField(to='User', on_delete=models.CASCADE, null=True)

---------------------
permissions.py 里面代码
from rest_framework.permissions import BasePermission

# 写一个权限类,继承BasePermission,重写has_permission方法,在方法中实现权限认证
# 如果有权限返回True 否则返回False

class CommonPermission(BasePermission):
    def has_permission(self, request, view):
        # 实现权限的控制,这么判断当前登录用户是谁?通过request.user拿
        # 原理是,经过认证后返回的两个值里面,第一个值当前登录用户对象给了request.user
        if request.user.user_type == 1:
            return True
        else:
            # 没有权限,向对象中放入一个属性 message 可以指定返回的中文信息
            self.message = '您是[%s] 您没有权限' % request.user.get_user_type_display()
   # 如果表模型中,字段参数是choices,就可以通过get_字段名_display 拿到choices对的中文
            return False

----------------------------------
总结

# 1 写一个权限类,继承BasePermission
# 2 重写has_permission方法,在该方法在中实现权限认证,在这方法中,request.user就是当前登录用户
# 3 如果有权限,返回True
# 4 没有权限,返回False,定制返回的中文: self.message='中文'


# 5 局部使用和全局使用

-局部:只在某个视图类中使用【当前视图类管理的所有接口】
class BookDetailView(ViewSetMixin, RetrieveAPIView):
	permission_classes = [CommonPermission]

-------------------------------------
-全局:全局所有接口都生效
    REST_FRAMEWORK = {'DEFAULT_PERMISSION_CLASSES': [
                'app01.permissions.CommonPermission'],
                     }

-如果要全局生效,必须配合局部禁用,在不需要权限认证的视图类里面,把permission_classes设为空列表

class BookDetailView(ViewSetMixin, RetrieveAPIView):
    permission_classes = []

class UserView(ViewSet):
    authentication_classes = []
    permission_classes = []

    @action(methods=['POST'], detail=False)
    def login(self, request):
        pass

.
.
.
.

3 频率组件

# 控制某个接口访问频率(次数),主要是为了防爬虫的

.

3.1 使用步骤

# 需求:查询所有接口,同一个ip一分钟只能访问5次
--------------------------------------
# 频率类,不继承BaseThrottle,继承SimpleRateThrottle,目的少写代码
# 重写SimpleRateThrottle里面的 get_cache_key 方法

from rest_framework.throttling import SimpleRateThrottle, BaseThrottle


class CommonThrottle(SimpleRateThrottle):
    # 需要配合一个类属性,属性值随便写
    # 但是配置文件中配置就需要用到它
    scope = 'rate888'

    def get_cache_key(self, request, view):
        # 返回什么就以什么做频率限制(可以返回IP或用户id )
        # 客户端的ip地址从request.
        return request.META.get('REMOTE_ADDR')
        # return request.user.pk  # 以用户id做限制
--------------------------------------
from app01.throttling import CommonThrottle


# 查所有
class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = []
    throttle_classes = [CommonThrottle]  # 局部配置

--------------------------------------
配置文件,配置频率参数单位 时h 分m 秒s 都可以用,根据自己需求来定次数

REST_FRAMEWORK = {'DEFAULT_THROTTLE_RATES': {'rate888': '5/m',},

    'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.CommonThrottle'],
                     }

--------------------------------------

总结
# 1 写一个频率类,继承SimpleRateThrottle
# 2 重写get_cache_key方法,返回什么,就以什么做限制----》ip,用户id做限制
# 3 配置一个类属性:scope = 'book_5_m'
# 4 在配置文件中配置
  'DEFAULT_THROTTLE_RATES': {
        'book_5_m': '5/m',
    },

# 5 局部使用和全局使用
-局部:只在某个视图类中使用【当前视图类管理的所有接口】
class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    throttle_classes = [CommonThrottle]  # 局部配置


-全局:全局所有接口都生效
REST_FRAMEWORK = {'DEFAULT_THROTTLE_RATES': {'rate888': '5/m',},

    'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.CommonThrottle'],
                     }

-要配合局部禁用,将不需要频率限制的接口的视图类里面,把throttle_classes设为空列表 :

class BookDetailView(ViewSetMixin, RetrieveAPIView):
            throttle_classes = []

class UserView(ViewSet):
    authentication_classes = []
    permission_classes = []
    throttle_classes = []

    @action(methods=['POST'], detail=False)
    def login(self, request):
        pass

.
.
.
.

4 过滤与排序

# restful规范中,要求了,请求地址中带过滤条件
	-注意: 5个接口中,只有一个接口需要有过滤和排序,就是查询所有接口!!

# 需求: 查询 所有图书接口,查询名字中有 红 这个字的所有图书

.
.

4.0 继承APIView 自己写(伪代码,自己补齐)

class BookView(APIView):
    def get(self,request):
        search=request.query_params.get('search')
        books=Book.objects.filter()

.
.

补充:3种过滤类不能一起共用,只能单用一个

.
.
.
.
.

4.1 内置过滤类的使用【必需继承GenericAPIView】 主要作用模糊搜索匹配!!

from rest_framework.filters import SearchFilter, OrderingFilter

class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    #  SearchFilter内置的,固定用法,模糊匹配
    #  就有过滤功能了,但需要指定用哪个字段过滤!
    filter_backends = [SearchFilter]
    search_fields = ['name','price']  # 可以按名字模糊匹配或价格模糊匹配
    # search_fields = ['name']  # 可以按名字模糊匹配

# 可以使用的搜索方式 路由后面必须要写成 ?search=XXX的形式
http://127.0.0.1:8000/api/v1/books/?search=红  # name或price中只要有红就会搜出来
http://127.0.0.1:8000/api/v1/books/?search=888  # name或price中只要有888就会搜出来
------------------------------------------
# 继承APIView如何写,完全自己写,麻烦,但是清晰

.
.
.
.
.

4.2 使用第三方djagno-filter实现过滤 只支持精准匹配过滤!!!

# 安装:django-filter
pip3.8 install django-filter==22.1 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com/simple/
腾讯源太垃圾了,用豆瓣

再把django版本往回降一点
pip3.8 install django==3.2
--------------------------------------
from django_filters.rest_framework import DjangoFilterBackend

class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = []
    authentication_classes = []
    throttle_classes = []

    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['name','price']  # 只支持完整匹配  name=聊斋11&price=933


# filterset_fields 括号里面只能写表里面的字段,关联表里面的字段不能往里写!!!
# 注意用第三方的过滤类里面用的就不是search_fields 了!!!
-------------------------------------------
# 支持的查询方式
http://127.0.0.1:8000/api/v1/books/?price=939
http://127.0.0.1:8000/api/v1/books/?price=939&name=红楼梦

.
.
.
.
.
.
.

4.3 自己定制过滤类实现过滤

主要适用于条件过滤的情况,或者用关联表里面的字段去进行过滤的情况

# 需求 查询价格大于100的所有图书
http://127.0.0.1:8000/api/v1/books/?price_gt=100

----------------------------------------
# 第一步; 定义一个过滤类,继承BaseFilterBackend,重写filter_queryset方法

class CommonFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        # 此处的行参view指代是BookView,原因不知道
        # 在里面实现过滤,返回qs对象,就是过滤后的数据
        price_gt = request.query_params.get('price_gt', None)
        if price_gt:
            qs = queryset.filter(price__gt=int(price_gt))
            return qs
        else:
            return queryset
----------------------------------
from app01.filter import CommonFilter

# 第二步:配置在视图类上
class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    authentication_classes = []
    permission_classes = []
    throttle_classes = []

    filter_backends = [CommonFilter,]  # 可以定制多个,从左往右,依次执行!
    # 自定义过滤类是不要写过滤字段的!!!


# 支持的查询方式(查询价格大于100元的书数据)
http://127.0.0.1:8000/api/v1/books/?price_gt=100
--------------------------------
注意模型表里面
price = models.CharField(max_length=32)  # CharField 就不支持查询的时候使用__gt 等筛选方法了,要改掉
price = models.IntegerField()

.
.
.
.
.
.
.
.

4.4 排序的使用

# 就用内置的就够了

from rest_framework.filters import SearchFilter, OrderingFilter

class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [OrderingFilter, CommonFilter]  # 既有排序,也有过滤功能
    ordering_fields = ['price''id']  # 按价格排序

# 支持的查询方法:
    # 价格大于100,并且价格从小到大排序!
    http://127.0.0.1:8000/api/v1/books/?price_gt=100&ordering=price

    # 价格从大到小排序!
    http://127.0.0.1:8000/api/v1/books/?ordering=-price

    # 价格从大到小排序,假如价格相同,再按id排序从小到大排序
    http://127.0.0.1:8000/api/v1/books/?ordering=-price,id

.
.
.
.
.
.
.
.
.

5 分页

#  分页,只有查询所有接口,才有分页

# drf内置了三个分页器,对应三种分页方式

#内置的分页类不能直接使用,需要继承,定制一些参数后才能使用
---------------------------------------------

# 分页使用,自定义一个分页类(三种继承)
---------------------------------------------
# 新建一个page.py文件,里面写自定义分页类代码

# PageNumberPagination 网页一般用它

class CommonPageNumberPagination(PageNumberPagination):
    page_size = 2  # 每页显示2条
    page_query_param = 'page'  # page = 10   查询第10页的数据,每页显示2条

    page_size_query_param = 'size'
    # 定制每页显示条数的参数 page=4&size=5  每页显示5条,并查询第4页的数据,不写&size=5就还是每页显示2条

    max_page_size = 4
    # 每页最大显示4条,上面定制的page=4&size=5 就变成了 每页显示4条 查询第5页的数据!!

# 控制每页显示的条数,max_page_size的参数的权重最大!!!
# pc网页端基本都用这种分页方式 http://127.0.0.1:8077/api/v1/books/?page=3&size=5
---------------------------------------------
---------------------------------------------
# LimitOffsetPagination

class CommonLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 2  # 每页显示2条
    limit_query_param = 'limit'  # limit=3  每页显示3条

    offset_query_param = 'offset'
    # offset=3&limit=3  从第1条位置开始偏移3条数据后,再往后显示3条数据

    max_limit = 5
    # offset=3&limit=10  从第1条位置开始偏移3条数据后,再往后显示5条数据


# 控制每页显示的条数,max_limit的参数的权重最大!!!
http://127.0.0.1:8077/api/v1/books/?offset=3&limit=5
自动生成下一页的路由  http://127.0.0.1:8077/api/v1/books/?offset=8&limit=5
# 从第1条位置开始偏移8条数据后,再往后显示5条数据
---------------------------------------------
---------------------------------------------

# CursorPagination  app一般用它
# 注意用这种游标分页,不能与内置的排序类OrderingFilter一起使用了,因为游标分页里面自带了分页了!!!

class CommonCursorPagination(CursorPagination):
    cursor_query_param = 'cursor'  # 查询的参数
    page_size = 2  # 每页显示多少条
    ordering = 'id'  # 排序字段 先将所有数据按id排序

# 在分页的过程中,会一直有一个游标指针的概念,比如当拿到第5页后,只能选择第5页往前或者往后一页的数据,其他页数据拿不到。
# 这样拿第10000页与拿第10页的速度是一样,
---------------------------------------------
---------------------------------------------

# 配置在视图类上即可
from app01.page import CommonPageNumberPagination,CommonLimitOffsetPagination,CommonCursorPagination

class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = []
    authentication_classes = []
    throttle_classes = []
    # 之前的东西一样用 ,内置的分页类不能直接使用,需要继承,定制一些参数后才能使用

    # pagination_class = CommonPageNumberPagination

    # pagination_class = CommonLimitOffsetPagination

    pagination_class = CommonCursorPagination
    # 游标分页,只能下一页,上一页,不能跳到中间,但它的效率最高,大数据量分页,使用这种较好

.
.
.
.

作业

# 编写认证类,权限类,频率类,登录接口
	-所有接口,一分钟访问10次
    -查询所有图书和查询单条图书不登录可以访问
    -新增,删除,修改必须登录后才能访问
    -删除功能只有超级管理员能删除
    
# 三种过滤方式实现能够搜索图书,带排序



-------------------------------------------
# 高级---部分同学做
	-继承BaseThrottle,重写allow_request,实现按ip,一分钟只能访问3#(1)取出访问者ip
    #(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
    #(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
    #(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
    #(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
    
    
# 基于APIView写分页

.

# 编写认证类,权限类,频率类,登录接口
	-所有接口,一分钟访问10次
    -查询所有图书和查询单条图书不登录可以访问
    -新增,删除,修改必须登录后才能访问
    -删除功能只有超级管理员能删除
# 三种过滤方式实现能够搜索图书,带排序

# 主要思路1:登录后才能访问的接口与不登录就可以访问的接口应该 分到两个视图类里面去
# 主要思路2:删除接口也单独分出来,不要和新增修改放到一个视图类里面去,不然权限校验没法单独加

# 写代码过程中犯的错:
序列化类里面局部钩子函数名validate_name  错写成validated_name   !!!
UserToken模型表的里面外键字段不小心注掉了,导致在序列化查单个与查所有的时候,定制化字段拿不到值,一直报错!!!
---------------------------------------------------
视图类主要代码

from rest_framework.views import APIView
from rest_framework.generics import ListAPIView, CreateAPIView, ListCreateAPIView
from rest_framework.generics import DestroyAPIView, UpdateAPIView, RetrieveAPIView, RetrieveUpdateDestroyAPIView
from rest_framework.generics import RetrieveUpdateAPIView, RetrieveDestroyAPIView
from rest_framework.viewsets import ViewSetMixin, ViewSet, GenericViewSet
from app01.models import Book, User, UserToken
from app01.serializer import BookSerializer
from rest_framework.decorators import action
import uuid
from rest_framework.response import Response
from app01.authenticate import LoginAuth
from app01.permissions import CommonPermission
from app01.filter import CommonFilter
from rest_framework.filters import SearchFilter, OrderingFilter


class SearchBookView(ViewSetMixin, ListAPIView, RetrieveAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    # filter_backends = [SearchFilter]
    # search_fields = ['name','price']  # 可以按名字模糊匹配或价格模糊匹配
    filter_backends = [CommonFilter, OrderingFilter]  # 可以定制多个,从左往右,依次执行!
    ordering_fields = ['price', 'id']  # 按价格从小到大排序,价格相同再按id排序


class SetBookView(ViewSetMixin, CreateAPIView, UpdateAPIView):
    authentication_classes = [LoginAuth]

    queryset = Book.objects.all()
    serializer_class = BookSerializer


class DeleteBookView(ViewSetMixin, DestroyAPIView):
    authentication_classes = [LoginAuth]
    permission_classes = [CommonPermission]

    queryset = Book.objects.all()
    serializer_class = BookSerializer


class UserView(ViewSetMixin, APIView):

    @action(methods=['POST'], detail=False)
    def login(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user_obj = User.objects.filter(username=username, password=password).first()
        if user_obj:
            token = str(uuid.uuid4())
            UserToken.objects.update_or_create(user=user_obj, defaults={'token': token})
            return Response({'code': 100, 'msg': '登录成功', 'token': token})
        else:
            return Response({'code': 101, 'msg': '用户名或密码错误'})

---------------------------------------

序列化类主要代码
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from app01.models import Book


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book

        fields = ['id', 'name', 'price', 'publish', 'authors', 'publish_detail', 'author_detail']

        extra_kwargs = {
            'publish_detail': {'read_only': True},
            'author_detail': {'read_only': True},
            'publish': {'write_only': True},
            'authors': {'write_only': True}
        }

    # 全局钩子
    def validate(self, attrs):
        if attrs.get('name') == attrs.get('publish'):
            raise ValidationError('书名不能和出版社一样')
        else:
            return attrs

    def validate_name(self, name_value):
        if name_value.startswith('sb'):
            raise ValidationError('书名不能以sb开头')
        else:
            return name_value

---------------------------------------------------
路由代码

from django.contrib import admin
from django.urls import path, include
from app01 import views
from rest_framework.routers import SimpleRouter, DefaultRouter

router = SimpleRouter()

router.register('user', views.UserView, 'user')  # /user/login  post请求
router.register('search_book', views.SearchBookView, 'search_book')
router.register('set_book', views.SetBookView, 'set_book')
router.register('del_book', views.DeleteBookView, 'del_book')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include(router.urls)),
]
---------------------------------------------------
模型层代码
from django.db import models


# Create your models here.


class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    user_type = models.IntegerField(choices=((1, '超级管理员'), (2, '普通用户'), (3, '垃圾用户')), default=2)


class UserToken(models.Model):
    token = models.CharField(max_length=32)
    user = models.OneToOneField(to='User', on_delete=models.CASCADE, null=True)


class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField()

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, null=True)
    authors = models.ManyToManyField(to='Author')

    @property
    def publish_detail(self):
        return {'name': self.publish.name, 'addr': self.publish.addr}

    @property
    def author_detail(self):
        return [{'name': author.name, 'phone': author.phone} for author in self.authors.all()]


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)


class Author(models.Model):
    name = models.CharField(max_length=32)
    phone = models.CharField(max_length=32)
	
--------------------------------------
认证类
from rest_framework.authentication import BaseAuthentication
from app01.models import UserToken
from rest_framework.exceptions import AuthenticationFailed


class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        token = request.query_params.get('token', None)
        if token:
            ut_obj = UserToken.objects.filter(token=token).first()
            if ut_obj:
                return ut_obj.user, token
            else:
                raise AuthenticationFailed('token认证失败')
        else:
            raise AuthenticationFailed('token没传')
--------------------------------------
权限类
from rest_framework.permissions import BasePermission

class CommonPermission(BasePermission):
    def has_permission(self, request, view):
        if request.user.user_type == 1:
            return True
        else:
            self.message = '您是[%s] 您没有权限' % request.user.get_user_type_display()
            return False
--------------------------------------
# 频率类
from rest_framework.throttling import SimpleRateThrottle, BaseThrottle

class CommonThrottle(SimpleRateThrottle):
    # 需要配合一个类属性,属性值随便写
    # 但是配置文件中配置就需要用到它
    scope = 'rate888'

    def get_cache_key(self, request, view):
        # 返回什么就以什么做频率限制(可以返回IP或用户id )
        # 客户端的ip地址从request.
        return request.META.get('REMOTE_ADDR')
        # return request.user.pk  # 以用户id做限制
--------------------------------------
# 过滤类
from rest_framework.filters import BaseFilterBackend

class CommonFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        price_gt = request.query_params.get('price_gt', None)
        if price_gt:
            q_set = queryset.filter(price__gt=int(price_gt))
            return q_set
        else:
            return queryset
--------------------------------------
配置
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False

REST_FRAMEWORK = {'DEFAULT_THROTTLE_RATES': {'rate888': '10/m', },
                  'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.CommonThrottle'],
                  }
posted @   tengyifan  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示