认证组件
## 认证组件逻辑 # 1.登录认证的限制: - 访问接口,有的需要登陆后才能访问,有的接口,不登录就能访问 # 2.准备: -User表 : 存储用户名,密码 -UserToken表:存储用户登录状态 """ UserToken表可以没有,如果没有,把字段直接写在User表上也可以 """ # 3.登录接口: -写个登录接口,返回token -之后只要带着token过来,就是登录,不带,就没有登录。 # 4.查询所有,不需要登录就能访问 # 5.查询单个,需要登录才能访问 ## 认证组件的使用步骤 # 1.写一个认证类,继承BaseAuthentication # 2.重写authenticate方法,在该方法中实现登录认证 # 3.如果认证成功,返回两个值【返回None或两个值】 # 4.认证不通过,抛异常AuthenticationFailed # 5.局部使用和全局使用 - 局部:只在某个视图类中使用【当前视图类管理的所有接口】 class BookDetailView(ViewSetMixin, RetrieveAPIView): authentication_classes = [LoginAuth] - 全局:全局所有接口都生效(登录接口不要) REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES':['app01.authenticate.LoginAuth'] } - 局部禁用: class BookDetailView(ViewSetMixin, RetrieveAPIView): authentication_classes = []
"""
注意:不要在配置文件中乱导入不使用的东西,否则会报错
"""
BookSerizlizer
## serizlizers.py from rest_framework import serializers from .models import Book class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = ['name','price','publish_detail','author_list','publish','authors'] extra_kwargs = {'name':{'max_length': 8}, 'publish_detail': {'read_only':True}, 'author_list': {'read_only':True}, 'publish': {'write_only': True}, 'authors': {'write_only': True},}
登录接口
## 表模型 class User(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=32) class UserToken(models.Model): # 跟User是一对一 token = models.CharField(max_length=32) user = models.OneToOneField(to='User', on_delete=models.CASCADE, null=True) # user :反向,表名小写,所有有user字段 ## 路由 from django.contrib import admin from django.urls import path, include from app01 import views from rest_framework.routers import SimpleRouter router = SimpleRouter() router.register('user',views.UserView,'user') # 路径: api/v1/user/login 向此发送post请求 router.register('books',views.BookView,'books') urlpatterns = [ path('admin/',admin.site.urls), path('api/v1/',include(router.urls)) ] ## 视图函数 from rest_framework.viewsets import ViewSet from rest_framework.decorators import action from .models import User,UserToken import uuid from rest_framework.response import Response ## 登录接口 自动生成路由,因为登录功能不用序列化,直接继承viewset class UserView(ViewSet): # 1.action装饰器 写在视图类的方法上,可以自动生成路由 @action(methods=['POST'],detail=False) def login(self,request): username = request.data.get('username') password = request.data.get('password') # 过滤用户名 user = User.objects.filter(username=username,password=password).first() if user: # 用户存在,登录成功,生成一个随机字符串,使用uuid生成 token = str(uuid.uuid4()) # 生成一个永不重复的随机字符串 # 在user_token表中存一下 # 1.从来没有登录过,插入一条记录 / 2.登陆过,修改记录 # 有就修改,没有新增 # 1.update_or_create内部: def update_or_create(self, defaults=None, **kwargs) # 通过kwargs传入的东西查找,能找到使用defaults的更新,否则新增一个 UserToken.objects.update_or_create(user=user, defaults={'token':token}) return Response({'code':'100','msg':'登录成功','token':token}) else: return Response({'code':'101','msg':'用户名或密码错误'})
查询单个和查询所有视图类的
## 路由 from django.contrib import admin from django.urls import path, include from app01 import views from rest_framework.routers import SimpleRouter router = SimpleRouter() router.register('books',views.BookView,'books') router.register('books',views.BookDetailView,'books') urlpatterns = [ path('admin/',admin.site.urls), path('api/v1/',include(router.urls)) ] ## 视图函数 from .models import Book from .serializer import BookSerializer from rest_framework.generics import ListAPIView,RetrieveAPIView from rest_framework.viewsets import ViewSetMixin from rest_framework.mixins import RetrieveModelMixin # 查询单个 # 方式一:分开写,查询所有+查询单个 class BookView(ViewSetMixin,ListAPIView): queryset = Book.objects.all() serializer_class = BookSerializer class BookDetailView(ViewSetMixin,RetrieveAPIView): queryset = Book.objects.all() serializer_class = BookSerializer # 方式二:合并写,查询单个+所有 class BookView(ViewSetMixin,ListAPIView,RetrieveModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer
认证类的编写与使用
## 路由urls.py from django.contrib import admin from django.urls import path, include from app01 import views from rest_framework.routers import SimpleRouter router = SimpleRouter() router.register('books',views.BookView,'books') router.register('books',views.BookDetailView,'books') router.register('user',views.UserView,'user') # 路径: api/v1/user/login 向此发送post请求 urlpatterns = [ path('admin/',admin.site.urls), path('api/v1/',include(router.urls)) ] ## 视图类views.py from rest_framework.viewsets import ViewSet from rest_framework.decorators import action from .models import User,UserToken import uuid from rest_framework.response import Response ## 登录接口 自动生成路由,因为登录功能不用序列化,直接继承viewset class UserView(ViewSet): @action(methods=['POST'],detail=False) def login(self,request): username = request.data.get('username') password = request.data.get('password') # 过滤用户名 user = User.objects.filter(username=username,password=password).first() if user: # 用户存在,登录成功,生成一个随机字符串,使用uuid生成 token = str(uuid.uuid4()) # 生成一个永不重复的随机字符串 # 在user_token表中存一下 # 1.从来没有登录过,插入一条记录 / 2.登陆过,修改记录 # 有就修改,没有新增 # 1.update_or_create内部: def update_or_create(self, defaults=None, **kwargs) # 通过kwargs传入的东西查找,能找到使用defaults的更新,否则新增一个 UserToken.objects.update_or_create(user=user, defaults={'token':token}) return Response({'code':'100','msg':'登录成功','token':token}) else: return Response({'code':'101','msg':'用户名或密码错误'}) from .models import Book from .serializer import BookSerializer from rest_framework.generics import ListAPIView,RetrieveAPIView from rest_framework.viewsets import ViewSetMixin from .authenticate import LoginAuth # 查询所有+查询单个 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] ## 自己写的认证类 # 自己写的认证类,需要继承某个类 from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from .models import UserToken # 继承之后需要重写里面的方法,BaseAuthentication内的方法(authenticate,authenticate_header) class LoginAuth(BaseAuthentication): def authenticate(self, request): # 在这里实现认证,如果是登陆的,继续往后执行返回两个值,如果不是抛出异常 # 请求中是否携带token,判断是否登录 【get请求请求体不能携带数据,排除】,放在地址栏中 token = request.query_params.get('token',None) if token: # 前端传入token,去表中查,如果能查到,登陆了返回两个值[当前登录用户,token]固定的 user_token = UserToken.objects.filter(token=token).first() if user_token: return user_token.user,token else: # 没有登录,抛异常[认证失败] raise AuthenticationFailed('token认证失败') else: raise AuthenticationFailed('没有传入token')
权限组件
# 1.即便登录成功了,有些接口,还是不能访问,因为没有权限 # 2.登录后,有的接口有权限访问,有的没有权限访问 # 3.查询单个和查询所有,都要登录才能访问----》全局认证 - 查询单个需要超级管理员才能访问 - 查询所有,所有登录用户都能访问 # 4.权限是一个字段,在User表中,加入user_type字段,做表迁移
- user_type = models.IntegerField(choices=((1,'超级管理员'),(2,'普通用户'),(3,'匿名用户')
权限类的编写和使用
## 模型类中 user表新增: user_type = models.IntegerField(choices=((1,'超级管理员'),(2,'普通用户'),(3,'匿名用户') ## 视图views.py from .permissions import CommonPermission class BookDetailView(ViewSetMixin,RetrieveAPIView): queryset = Book.objects.all() serializer_class = BookSerializer # 登录需要写一个认证类,自己写 authentication_classes = [LoginAuth] # 查询单个要权限类 permission_classes = [CommonPermission] ## 重写权限类permissions.py # 写一个类,继承基类BasePermission,重写has_permission[读源码]方法,在方法中实现权限认证,如果有权限return Ture,没有权限return False from rest_framework.permissions import BasePermission class CommonPermission(BasePermission): def has_permission(self, request, view): # 实现权限控制 ---> 知道当前登录用户是request.user if request.user.user_type==1: return True else: # 没有权限,向对象中放一个属性 # 如果表模型中使用了choice,就可以通过get_字段名_display() 拿到choice对应的中文 self.message = '您是%s,您没有权限' %request.user.get_user_type_display() 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', ],} -局部禁用: class BookDetailView(ViewSetMixin, RetrieveAPIView): permission_classes = []
频率组件
控制某个接口访问频率(次数)
# 查询所有接口,同一个ip一分钟只能访问5次 ## 视图类views.py from .throttling import CommonThrottle # 方式一:分开写,查询所有+查询单个 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] # 查询单个要权限类 permission_classes = [CommonPermission] # 限制访问频率次数频率类 throttle_classes = [CommonThrottle] ## 频率类throttling.py # 频率类,不继承BaseThrottle,继承SimpleRateThrottle,写的代码少 from rest_framework.throttling import SimpleRateThrottle # 继承SimpleRateThrottle必须重写get_cache_key class CommonThrottle(SimpleRateThrottle): # 配合类属性 scope = 'book_5_m' # 配置文件中配置,要在settings.py里面配置一些配置 def get_cache_key(self, request, view): # 返回什么,就以什么做频率限制(一般以ip或者用户id作为限制) # 客户端ip地址从哪里拿? >>>: request.META:是一个标准的python字典,包含http请求头 >>>:REMOTE_ADDR :客户端ip地址 # return request.META.get('REMOTE_ADDR') # 以IP做限制 return request.user.pk # 以用户id做限制
控制访问频率次数的步骤
# 1 写一个频率类,继承SimpleRateThrottle # 2 重写get_cache_key方法,返回什么,就以什么做限制----》ip,用户id做限制 # 3 配置一个类属性:scope = 'book_5_m' # 4 在配置文件中配置 'DEFAULT_THROTTLE_RATES': { 'book_5_m': '5/m', }, # 5 局部使用和全局使用 -局部:只在某个视图类中使用【当前视图类管理的所有接口】 class BookDetailView(ViewSetMixin, RetrieveAPIView): throttle_classes = [CommonThrottle] -全局:全局所有接口都生效 REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.CommonThrottle'], } -局部禁用: class BookDetailView(ViewSetMixin, RetrieveAPIView): throttle_classes = []
过滤排序
继承APIView自定义【伪代码,未补齐】
# 继承APIView后,过滤完全需要自己写,会更麻烦,但是清晰 class BookView(APIView): def get(self,request): search=request.query_params.get('search') books=Book.objects.filter()
内置过滤类的使用【继承GenericAPIView】
# restful规范中,要求了,请求地址中带过滤条件 - 5个接口中,只有查询所有接口 接口需要有过滤和排序 ## views.py """注掉之前代码,避免影响""" # 过滤,必须继承GenericAPIView及其子类,才可以使用这种方式[配置过滤类的方式] # 导入过滤排序类,过滤类SearchFilter,排序类OrderingFilter from rest_framework.filters import SearchFilter,OrderingFilter class BookView(ViewSetMixin,ListAPIView): queryset = Book.objects.all() serializer_class = BookSerializer authentication_classes = [] permission_classes = [] throttle_classes = [] # 内置的filter_backends,是GenericAPIView的,配上过滤类就有了过滤功能 filter_backends =[SearchFilter] # 过滤有三层:内置,第三方,自己写的 # 可以按名字模糊去匹配 search_fields = ['name'] # 可以按名字或价格模糊去匹配 search_fields=['name','price'] # 可以使用的搜索方式 http://127.0.0.1:8000/api/v1/books/?search=h # search=h 代表name或price中只要有h就会搜出来
使用第三方django-filter实现过滤
# 1.安装 django-filter pip install django-filter # 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=xx11&price=xxx # 支持的查询方式 http://127.0.0.1:8000/api/v1/books/?price=939 http://127.0.0.1:8000/api/v1/books/?price=939&name=红楼梦
自定义定制过滤类实现过滤
#第一步; 定义一个过滤类,继承BaseFilterBackend,重写filter_queryset方法 class CommonFilter(BaseFilterBackend): def filter_queryset(self, request, queryset, view): # 在里面实现过滤,返回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 #第二步:这里要吧模型类models.py内的Book表price字段做修改 - 将CharField变为IntegerField后迁移 # 第三步:配置在视图类views.py上 class BookView(ViewSetMixin, ListAPIView): queryset = Book.objects.all() serializer_class = BookSerializer filter_backends = [CommonFilter] # 可以定制多个,从左往右,依次执行
排序的使用
## 排序使用内置的就够了 from rest_framework.filters import OrderingFilter # 需要配置在filter_backends上 class BookView(ViewSetMixin, ListAPIView): queryset = Book.objects.all() serializer_class = BookSerializer filter_backends = [OrderingFilter] ordering_fields = ['price'] # 支持的查询方法: - http://127.0.0.1:8000/api/v1/books/?ordering=price - http://127.0.0.1:8000/api/v1/books/?ordering=-price - http://127.0.0.1:8000/api/v1/books/?ordering=-id,price
分页
# 1.分页·:只有查询所有的接口,才有分页 # 2.drf内置了三个分页器,对应了三种分页方式 # 3.内置的分页类不可以直接使用,需要继承,定制一些参数后才可以使用 #4.分页使用三种方法: ## 分页类: page.py from rest_framework.pagination import LimitOffsetPagination,PageNumberPagination,CursorPagination # 方法一: class CommonPageNumberPagination(PageNumberPagination): page_size = 2 # 每页显示2条 page_query_param = 'page' # page=10 查询第10页的数据,每页显示2条 page_size_query_param = 'size' # page=10&size=5 查询第10页,每页显示5条 max_page_size = 5 # 每页最大显示10条 # 方式二:LimitOffset class CommonLimitOffsetPagination(LimitOffsetPagination): default_limit = 3 # 每页显示2条 limit_query_param = 'limit' # limit=3 取3条 offset_query_param = 'offset' # offset=1 从第一个位置开始,取limit条 max_limit = 5 # offset=3&limit=2 0 1 2 3 4 5 ## 方法三:app 用下面的类 class CommonCursorPagination(CursorPagination): cursor_query_param = 'cursor' # 查询参数 page_size = 2 # 每页多少条 ordering = 'id' # 排序字段 ## 视图类views.py # 分页功能,必须是继承GenericAPIView,如果继承APIView要自己写 from .page import CommonPageNumberPagination as PageNumberPagination\ ,CommonCursorPagination as CommonCursorPagination,\ CommonLimitOffsetPagination as LimitOffsetPagination class BookView(ViewSetMixin,ListAPIView): queryset = Book.objects.all() serializer_class = BookSerializer authentication_classes = [] permission_classes = [] throttle_classes = [] # 自己定义分页类,内置的分页类不能直接使用,需要继承,定制一些参数后才能使用 # 偏移分页 http://127.0.0.1:8000/api/v1/books/?limit=4&offset=1 : 从第一条开始,取4条 pagination_class = LimitOffsetPagination # 游标分页,只能下一页,上一页,不能跳到中间,但它的效率最高,大数据量分页,使用这种较好 pagination_class = CommonCursorPagination # pagination_class = PageNumberPagination
标签:仙界,登录,framework,token,user,import,drf,class,07 From: https://www.cnblogs.com/juzijunjun/p/17099976.html