【认证】

  models.py
 1 from django.db import models
 2 
 3 
 4 # Create your models here.
 5 class User(models.Model):
 6     username = models.CharField(max_length=50)
 7     password = models.CharField(max_length=50)
 8     user_type = models.IntegerField(choices=((1, '普通用户'), (2, '普通管理员'), (3, '超级管理员')), default=1)
 9   # 认证登录的用户是否是user里的用户
10     @property
11     def is_authenticated(self):
12         return True
13 
14 
15 class UserToken(models.Model):
16     user = models.OneToOneField(User, on_delete=models.CASCADE)
17     token = models.CharField(max_length=500)
18 
19 
20 class Book(models.Model):
21     name = models.CharField(max_length=50)
22     publish = models.CharField(max_length=50)
23     price = models.IntegerField()
24 
25     def __str__(self):
26         return self.name

 

VIEWS.PY

# ============================================认证类
# 登录接口
class UserView(ViewSet):
    authentication_classes = []
    # 登录接口局部禁用
    permission_classes = []

    @action(methods=['POST'], detail=False)
    def login(self, request):
        # 用户名,密码在request.data中
        username = request.data.get('username')
        password = request.data.get('password')
        # 验证用户名和密码
        user = User.objects.filter(username=username, password=password).first()
        if user:
            # 验证通过,生成随机字符串,存到usertoken中
            # 之前存在就修改,没有就新增
            token = str(uuid.uuid4())
            UserToken.objects.update_or_create(user=user, defaults={'token': token})
            return Response({'code': 200, 'msg': '登录成功', 'token': token})
        else:
            return Response({'code': 400, 'msg': '用户名或密码错误'})

=======================================================
authenticate.py 里面代码
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed

from app05.models import UserToken


class LoginAuth(BaseAuthentication):
  重写父类里面的authenticate方法
def authenticate(self, request):
# request是新的,从request中取出用户携带的token
    # 放在请求头中
        token = request.META.get('HTTP_TOKEN')
user_token = UserToken.objects.filter(token=token).first()
if user_token:
user = user_token.user
return user, token
else:
raise AuthenticationFailed('认证失败')

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

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

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

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

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

 

.。




(权限)
views.py
# 权限,需要普通用户以上才能访问,但是必须要认证通过
class BooksView(GenericViewSet, ListModelMixin, DestroyModelMixin, CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    # authentication_classes = [LoginAuth]
  # 局部限制
  permission_classes = []

===========================================
permissions.py文件
from rest_framework.permissions import BasePermission

# 权限,只有超级用户才能访问,其他用户没有权限
class UserPermission(BasePermission):
def has_permission(self, request, view):
# 判断权限,如果有权限,返回True,否则返回False
# 认证通过,拿到当前登录用户,request.user中拿到
# is_authenticated在用户表中没有,需要去重新定义
if request.user.is_authenticated:
if request.user.user_type == 3:
return True
else:
# 定制提示信息,get_user_type_display拿到数字对应的字符串
user_type = request.user.get_user_type_display()
self.message = f'你是{user_type}没有权限访问'
return False
else:
self.message = '请先登录'
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设为空列表

 

(频率限制)

views.py


# 需求:查询所有接口,同一个ip一分钟只能访问3次

# 频率类,不继承BaseThrottle,继承SimpleRateThrottle,目的少写代码
# 重写SimpleRateThrottle里面的 get_cache_key 方法

class
PublishView(GenericViewSet): # 方式一:做限制,局部限制使用 # permission_classes = [UserPermission] # 频率限制使用 throttle_classes = [CustomThrottle] def list(self, request): return Response('所有数据')

================================
throttling.py文件
# 频率得使用

from rest_framework.throttling import BaseThrottle, SimpleRateThrottle

class CustomThrottle(SimpleRateThrottle):
# 1分钟3次
rate = '3/m'
def get_cache_key(self, request, view):
# 返回什么,就会以什么做限制---ip地址限制:用户id
return request.META.get('REMOTE_ADDR')
--------------------------------------

总结
# 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设为空列表 :


 

(分页。排序。过滤)

 1 # 查询所有图书接口
 2 class BookListView(GenericViewSet, ListModelMixin):
 3     queryset = Book.objects.all()
 4     serializer_class = BookSerializer
 5     permission_classes = []
 6     throttle_classes = []
 7     # 分页
 8     # pagination_class = CommonPageNumberPagination
 9     # pagination_class = CommonLimitOffsetPagination
10     pagination_class = CommonCursorPagination
11 
12     # 排序,必须继承GenericAPIView,在视图类中配置
13     # filter_backends = [OrderingFilter]
14     # 按照哪个字段排序,默认按照id排序,按照字段排序以后是按照价格升序,如果想降序:ordering=-price
15     # ordering_fields = ['price']
16 
17     # 过滤,可以和排序一起用
18     # filter_backends = [OrderingFilter, SearchFilter]
19     # ordering_fields = ['price', 'name']
20     # # 也可以多个字段模糊匹配
21     # search_fields = ['name', 'publish']
22 
23     # 第三方过滤==============按照名字精准匹配  pip3 install django-filter
24     # filter_backends = [OrderingFilter, DjangoFilterBackend]
25     # filterset_fields = ['name', 'price']
26 
27     # 自定义过滤
28     filter_backends = [CommonFilter]
29 
30     # 排序之定制返回格式
31     # def list(self, request, *args, **kwargs):
32     #     qs = self.get_queryset()
33     #     ser = BookSerializer(instance=qs, many=True)
34     #     return Response({'code': 200, 'msg': '请求成功', 'results': ser.data})

================================================================================================分页
page.py
from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination, CursorPagination


# 基本分页
class CommonPageNumberPagination(PageNumberPagination):
page_size = 2 # 每页显示2条
page_query_param = 'page' # http://127.0.0.1:8008/app01/api/v1/books1/?page=2
page_size_query_param = 'size' # http://127.0.0.1:8008/app01/api/v1/books1/?page=2&size=3 查询第二页,每页显示3条
max_page_size = 10 # 每页最多显示10条


# 偏移分页
class CommonLimitOffsetPagination(LimitOffsetPagination):
default_limit = 2 # 每页显示2条
limit_query_param = 'limit' # 控制每页显示多少条
# http://127.0.0.1:8013/app05/api/v1/books1/?offset=3 从第3条开始,取两条
# http://127.0.0.1:8013/app05/api/v1/books1/?offset=3&limit=1 从第3条开始,取1条
offset_query_param = 'offset' # 偏移量
max_limit = 10


# 游标分页
class CommonCursorPagination(CursorPagination):
cursor_query_param = 'cursor' # 查询条件 http://127.0.0.1:8008/app01/api/v1/books1/?cursor=asfasf
page_size = 2
ordering = 'id' # 排序字段,只能上一页,下一页这样选择,聊率高,字段必须是模型表中有的

==========================================================================================过滤
# 自定义过滤类
from django.db.models import Q
from rest_framework.filters import BaseFilterBackend


class CommonFilter(BaseFilterBackend):
# 必须重写这个方法
def filter_queryset(self, request, queryset, view):
# 完成过滤,返回 qs对象
# 查询价格为66 或者 名字中包含海的数据
# http://127.0.0.1:8008/app01/api/v1/books1/?price=42&name=红
price = request.query_params.get('price', None)
name = request.query_params.get('name', None)
if price and name:
queryset = queryset.filter(Q(price=price) | Q(name__contains=name))
if price:
# queryset=queryset.filter(Q(price=price) | Q(name__contains=name))
queryset = queryset.filter(price=price)
if name:
queryset = queryset.filter(name__contains=name)

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

 

 

 

路由

 全局生效的settings里面的配置

 



posted on 2024-04-17 20:35  认真的六六  阅读(19)  评论(0编辑  收藏  举报