翼度科技»论坛 编程开发 python 查看内容

django框架之drf(部分讲解)

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
目录

一、认证组件

简介:

  • 登录认证的限制
  • 认证组件是drf框架给我们提供的认证接口,它能够在请求进入视图函数/类前进验证(例如:认证用户是否登录),对不符合认证的请求进行拦截并返回校验失败的信息
(1)、登录接口
  1. # 认证是基于登录的接口上面操作的 所以前戏编写一个简单的登录接口
  2. models.py
  3. class User(models.Model):  # 简易的用户信息账号密码
  4.     username = models.CharField(max_length=32)
  5.     password = models.CharField(max_length=32)
  6.     def __str__(self):
  7.         return self.username
  8.         '跟User表是一对一外键关联,存储用户登录状态用的 [这个表可以没有,如果没有,把字段直接写在User表上也可以]'
  9. class UserToken(models.Model):  # 用户信息登录记录表
  10.     user = models.OneToOneField(to='User', on_delete=models.CASCADE)  # 一对一关联
  11.     token = models.CharField(max_length=32, null=True)  # 如果用户没有登录则没有值 如果登录则有值
  12. views.py
  13.         '登录接口功能:自动生成路由+登录功能,不用序列化,因此继承ViewSet即可'
  14. class UserView(ViewSet):
  15.     @action(methods=['POST'], detail=False, url_path='login', url_name='login')
  16.     def login(self, request):
  17.         username = request.data.get('username')     # 获取用户名与密码
  18.         password = request.data.get('password')
  19.         user = User.objects.filter(username=username, password=password).first()    # 比对用户名与密码
  20.         if user:
  21.             token = str(uuid.uuid4())  
  22.             # uuid4 随机获得永不重复的字符串 机制跟Cookie中的验证码一样
  23.             # 在userToken表中存储一下:1 从来没有登录过,插入一条,     2 登录过,修改记录
  24.             
  25.             
  26.             UserToken.objects.update_or_create(defaults={'token': token}, user=user)
  27.             # 通过user去UserToken表中查数据,如果能查到,使用defaults的数据更新,如果查不到,直接通过user和defaults的数据新增
  28.             # kwargs 传入的东西查找,能找到,使用defaults的更新,否则新增一条
  29.             return Response({'code': 100, 'msg': '登录成功', 'token': token})
  30.         else:
  31.             return Response({'code': 101, 'msg': '用户名或密码错误'})
  32. urls.py
  33.         from rest_framework.routers import SimpleRouter, DefaultRouter
  34.         router = SimpleRouter()
  35.         router.register('users', views.UserView, 'users')
  36.         urlpatterns += router.urls
  37. '''这个时候一个简单的登录接口就写好了 每次登录都会更新Token 相当于登录了之前的设备就无效了 '''
复制代码

update_or_create源码如下:
  1.     def update_or_create(self, defaults=None, **kwargs):
  2.         defaults = defaults or {}
  3.         self._for_write = True
  4.         with transaction.atomic(using=self.db):
  5.             try:
  6.                 obj = self.select_for_update().get(**kwargs)
  7.             except self.model.DoesNotExist:
  8.                 params = self._extract_model_params(defaults, **kwargs)
  9.                 obj, created = self._create_object_from_params(kwargs, params, lock=True)
  10.                 if created:
  11.                     return obj, created
  12.             for k, v in defaults.items():
  13.                 setattr(obj, k, v() if callable(v) else v)
  14.             obj.save(using=self.db)
  15.         return obj, False
复制代码
(2)、认证组件使用步骤

1.需要写一个认证类,因此我们需要在应用中另外创建一个py文件编写认证类,需要继承BaseAuthentication这个类


  • 通过查看源码我们可以发现有个authenticate方法需要我们重写,否则就会报错,这就是我们需要编写认证功能的类
  1. class BaseAuthentication:
  2.     def authenticate(self, request):
  3.         raise NotImplementedError(".authenticate() must be overridden.")
  4.     def authenticate_header(self, request):
  5.         pass
复制代码
2.重写authenticate方法,在该方法在中实现登录认证


  • token在哪带的?如何认证它是登录了的?
  • 用token来判断是否登陆,登陆了在访问的时候带上token,目前阶段我们直接在地址栏中携带token的数据,后面可以在请求头中添加token的数据
3、如果认证成功,返回两个值【返回None或两个值(固定的:当前登录用户,token)】
4、认证不通过,用AuthenticationFailed类抛异常
代码如下:
authenticate.py(认证类)
  1. # 自己写的认证类,继承某个类
  2. from rest_framework.authentication import BaseAuthentication
  3. from rest_framework.exceptions import AuthenticationFailed
  4. from .models import UserToken
  5. class LoginAuth(BaseAuthentication):
  6.     def authenticate(self, request):
  7.         # 在这里实现认证,如果是登录的,继续往后走返回两个值,如果不是抛异常
  8.         # 请求中是否携带token,判断是否登录,放在地址栏中
  9.         token = request.query_params.get('token', None) # 查找是否有token这个变量名的值,如果没有就返回None,默认好像返回的是数字
  10.         if token:  # 前端传入token了,去表中查,如果能查到,登录了,返回两个值[固定的:当前登录用户,token]
  11.             user_token = UserToken.objects.filter(token=token).first()
  12.             if user_token:
  13.                 return user_token.user, token
  14.             else:
  15.                 # 没有登录抛异常
  16.                 raise AuthenticationFailed('token认证失败')
  17.         else:
  18.             raise AuthenticationFailed('token没传')
  19. # 前端传入的请求头中的数据从哪取?  GET,body,POST,data
复制代码
5、认证类的使用


  • 当我们编写好了认证类中的认证代码,接着就需要导入到视图层然后使用他
  1. from rest_framework.generics import ListAPIView, RetrieveAPIView
  2. from rest_framework.viewsets import ViewSetMixin
  3. from .authenticate import LoginAuth
  4. # 查询所有
  5. class BookView(ViewSetMixin, ListAPIView):
  6.     queryset = Book.objects.all()
  7.     serializer_class = BookSerializer
  8.    
  9.    
  10. class BookDetailView(ViewSetMixin, RetrieveAPIView):
  11.     queryset = Book.objects.all()
  12.     serializer_class = BookSerializer
  13.     authentication_classes = [LoginAuth]  # 需要写一个认证类,需要咱们自行编写
复制代码
6、局部使用和全局使用


  • 局部使用:只在某个视图类中使用【当前视图类管理的所有接口】
  1. class BookDetailView(ViewSetMixin, RetrieveAPIView):
  2.         authentication_classes = [LoginAuth]
复制代码

  • 全局使用:在配置文件settings.py中编写,全局所有接口都生效
  1. REST_FRAMEWORK = {
  2.             'DEFAULT_AUTHENTICATION_CLASSES':['app01.authenticate.LoginAuth']
  3.         }
复制代码
注意事项:不要在配置文件中乱导入不使用的东西,否则会报错,但是在导入类似认证类这样的文件时,可以写上导入的代码然后再修改,最后写进配置中,这样可以减少错误

  • 局部禁用:(登陆接口很明显是不需要校验是否登陆的,因此有了这个局部禁用的需求,我们把他的authentication_classes配置成空就是局部禁用)
  1. class BookDetailView(ViewSetMixin, RetrieveAPIView):
  2.         authentication_classes = []
复制代码
7、测试路由参考

(3)、整体代码

views.py
  1. # 查询所有
  2. class BookView(ViewSetMixin, ListAPIView):
  3.     queryset = Book.objects.all()
  4.     serializer_class = BookSerializer
  5. # 查询单个
  6. class BookDetailView(ViewSetMixin, RetrieveAPIView):
  7.     queryset = Book.objects.all()
  8.     serializer_class = BookSerializer
  9.     authentication_classes = [LoginAuth]  # 需要写一个认证类,需要咱们自行编写
复制代码
authenticate.py(认证类)
  1. # 自己写的认证类,继承某个类
  2. from rest_framework.authentication import BaseAuthentication
  3. from rest_framework.exceptions import AuthenticationFailed
  4. from .models import UserToken
  5. class LoginAuth(BaseAuthentication):
  6.     def authenticate(self, request):
  7.         # 在这里实现认证,如果是登录的,继续往后走返回两个值,如果不是抛异常
  8.         # 请求中是否携带token,判断是否登录,放在地址栏中
  9.         token = request.query_params.get('token', None) # 查找是否有token这个变量名的值,如果没有就返回None,默认好像返回的是数字
  10.         if token:  # 前端传入token了,去表中查,如果能查到,登录了,返回两个值[固定的:当前登录用户,token]
  11.             user_token = UserToken.objects.filter(token=token).first()
  12.             if user_token:
  13.                 return user_token.user, token
  14.             else:
  15.                 # 没有登录抛异常
  16.                 raise AuthenticationFailed('token认证失败')
  17.         else:
  18.             raise AuthenticationFailed('token没传')
  19. # 前端传入的请求头中的数据从哪取?  GET,body,POST,data
复制代码
urls.py
  1. from django.contrib import admin
  2. from django.urls import path, include
  3. from app01 import views
  4. from rest_framework.routers import SimpleRouter
  5. router = SimpleRouter()  # 后面这个少的用的多,
  6. router.register('user', views.UserView, 'user')
  7. router.register('books', views.BookView, 'books')
  8. router.register('books', views.BookDetailView, 'books')
  9. urlpatterns = [
  10.     path('admin/', admin.site.urls),
  11.     path('api/v1/', include(router.urls)),
  12. ]
复制代码
权限组件

简介:

  • 在我们使用的一些app或者网页中(爱奇艺,腾讯视频),都会有一些会员接口(需要购买会员才能够使用或者观看),权限组件就是对用户的这一权限进行验证,在请求进入视图类/函数代码前进行校验,校验失败后直接将请求拦截,并返回校验失败的信息
(1)、权限组件的使用步骤

模块地址:
  1. from rest_framework.permissions import BasePermission
复制代码
用法简介:
  1. # 1、创建一个专门用于编写权限组件的py文件,写一个权限类,继承BasePermission
  2. # 2、重写has_permission方法(在该方法在中实现权限认证,在这方法中,request.user就是当前登录用户)
  3. # 3、如果有权限,返回True
  4. # 4、没有权限,返回False(定制返回的中文: self.message='中文')
  5. # 5、局部使用和全局使用
  6.         -局部使用: # 在某个视图类中设置接口(不会影响别的视图类)
  7.         class BookDetailView(ViewSetMixin, RetrieveAPIView):
  8.             permission_classes = [CommonPermission]
  9.         -全局使用:  # django的settings.py中配置,影响全局
  10.             REST_FRAMEWORK = {
  11.             'DEFAULT_PERMISSION_CLASSES': [
  12.                 'app01.permissions.CommonPermission',
  13.             ],
  14.         }
  15.   
  16.         -局部禁用:# 全局配置局部禁用
  17.         class BookDetailView(ViewSetMixin, RetrieveAPIView):
  18.             permission_classes = []
复制代码
(2)、代码用法

models.py(修改User表的配置后需要重新进行数据库迁移)
  1. class User(models.Model):
  2.     username = models.CharField(max_length=32)
  3.     password = models.CharField(max_length=32)
  4.     user_type = models.IntegerField(choices=((1, '超级管理员'), (2, '普通用户'), (3, '2B用户')), default=2)
复制代码
perssion.py(权限类代码)
  1. # 写权限类,写一个类,继承基类BasePermission,重写has_permission方法,在方法中实现权限认证,如果有权限return True ,如果没有权限,返回False
  2. from rest_framework.permissions import BasePermission
  3. class CommonPermission(BasePermission):
  4.     def has_permission(self, request, view):
  5.         # 实现权限的控制  ---》知道当前登录用户是谁?当前登录用户是  request.user
  6.         if request.user.user_type == 1:
  7.             return True
  8.         else:
  9.             # 没有权限,向对象中放一个属性 message
  10.             # 如果表模型中,使用了choice,就可以通过  get_字段名_display()  拿到choice对应的中文
  11.             self.message = '您是【%s】,您没有权限' % request.user.get_user_type_display()
  12.             return False
复制代码
views.py(视图类代码)
  1. class BookView(ModelViewSet):
  2.     queryset = Book.objects.all()
  3.     serializer_class = BookSerializer
  4.     # 局部认证
  5.     authentication_classes = [LoginAuth]
  6.     # 权限认证(将编写的频率类导入过来)
  7.     permission_classes = [CommentPermission]
复制代码

三、频率组件

简介:

  • 频率是指,控制某个接口访问频率(次数)
(1)、频率组件的使用步骤

模块地址:
  1. from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
  2. # BaseThrottle:需要手动编写的代码较多
  3. # SimpleRateThrottle: 需要手动编写的代码较少(用这个)
复制代码
用法简介
  1. # 1、创建一个专门用来编写频率组件的py文件,写一个频率类,继承SimpleRateThrottle
  2. # 2、重写get_cache_key方法,返回什么,就以什么做限制----》ip(用户id做限制)
  3. # 3、配置一个类属性scope = 'book_5_m'
  4. # 4、在django的配置文件中编写频率次数
  5.         REST_FRAMEWORK = {
  6.           'DEFAULT_THROTTLE_RATES': {
  7.             'book_5_m': '5/m',  # 一分钟五次
  8.         },
  9.             }
  10. # 5、局部使用和全局使用
  11.         -局部使用: # 只影响当前的视图类
  12.     class BookView(ModelViewSet):
  13.         throttle_classes = [CommentThrottle]
  14.      
  15.     -全局配置:影响全局
  16.             REST_FRAMEWORK = {
  17.              'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.CommonThrottle'],
  18.         }
  19.      
  20.     -局部禁用:
  21.       class BookView(ModelViewSet):
  22.          throttle_classes = [CommentThrottle]
复制代码
(2)、代码用法

throttle.py(频率类代码)
  1. from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
  2. class CommentThrottle(SimpleRateThrottle):
  3.     # 创建一个用于控制频率的变量名(需要传入配置文件)
  4.     scope = 'book_5_m'
  5.     def get_cache_key(self, request, view):
  6.         # 返回什么就以什么做限制(request.META.get('REMOTE_ADDR')以IP做限制)
  7.         return request.META.get('REMOTE_ADDR')
复制代码
视图类代码
  1. class BookView(ModelViewSet):
  2.     queryset = Book.objects.all()
  3.     serializer_class = BookSerializer
  4.     # 频率组件
  5.     throttle_classes = [CommentThrottle]
复制代码
django配置文件
  1. REST_FRAMEWORK = {
  2.     # 控制访问频率
  3.     'DEFAULT_THROTTLE_RATES': {
  4.         'book_5_m': '5/m',  # 一分钟五次
  5.     },
  6. }
复制代码

四、过滤的多种用法

简介:

  • 过滤是指在使用查询的时候,我们可以通过条件来过滤掉不需要的内容
  1. # restful规范中,要求了,请求地址中带过滤条件
  2.         -5个接口中,只有一个接口需要有过滤和排序,查询所有接口
复制代码
(1)、继承APIView自己写
  1. class BookView(APIView):
  2.     def get(self,request):
  3.         # 获取get请求携带的参数
  4.         name=request.query_params.get('name')
  5.         # 通过filter进行过滤
  6.         books = Book.objects.filter(name=name)
复制代码
(2)、使用drf的内置过滤(继承GenericAPIview)

模块地址:
该方法为模糊查询
  1. from rest_framework.filters import SearchFilter
复制代码
代码用法:
  1. class BookView(ModelViewSet):
  2.     queryset = Book.objects.all()
  3.     serializer_class = BookSerializer
  4.     # 实例化过滤对象
  5.     filter_backends = [SearchFilter]
  6.     # 指定过滤的字段(模糊查询)
  7.     search_fields = ['name', 'price']
复制代码
搜索方式:
  1. # name或price中只要有关键字就会搜出来 (只能用search=xxx的方式)
  2.         http://127.0.0.1:8000/api/v1/books/?search=西游记
复制代码

(3)、使用第三方插件过滤(精准过滤)

第三方插件:
  1. # 插件名称:
  2.         django-filter
  3.    
  4. # 安装插件:
  5.         pip3.8 install django-filter
复制代码
模块地址:
  1. from django_filters.rest_framework import DjangoFilterBackend
复制代码
代码用法:
  1. from rest_framework.viewsets import ModelViewSet
  2. from .models import Book
  3. from .serializer import BookSerializer
  4. from django_filters.rest_framework import DjangoFilterBackend
  5. class BookView(ModelViewSet):
  6.     queryset = Book.objects.all()
  7.     serializer_class = BookSerializer
  8.     # 第三方过滤插件
  9.     filter_backends = [DjangoFilterBackend]
  10.     # 查询的字段
  11.     filterset_fields = ['pk','name', 'price']
复制代码
搜索方式:
  1. http://127.0.0.1:8000/api/books/?price=99
  2. http://127.0.0.1:8000/api/books/?price=99&name=呐喊
复制代码

4、使用过滤组件

1.定制过滤组件的使用方式与步骤
模块地址:
  1. from rest_framework.filters import BaseFilterBackend
复制代码
用法简介:
  1. # 1、创建一个专门用于过滤的py文件,写一个类继承BaseFilterBackend
  2. # 2、重写filter_queryset方法,在方法内部进行过滤
  3. # 3、直接返回过滤后的对象
  4. # 4、如果没有过滤直接返回所有数据
  5. # 5、局部使用
  6.          -局部使用:
  7.     class BookView(ViewSetMixin, ListAPIView):
  8.         queryset = Book.objects.all()
  9.         serializer_class = BookSerializer
  10.         # 在类表内填写过滤的类
  11.         filter_backends = [CommonFilter]  # 可以定制多个,从左往右,依次执行
复制代码
2.代码用法
过滤类代码filters.py
  1. from rest_framework.filters import BaseFilterBackend
  2. class CommonFilter(BaseFilterBackend):
  3.     # 重写的类,编写过滤吧的内容
  4.     def filter_queryset(self, request, queryset, view):
  5.         # 获取过滤的条件
  6.         filter_comment = request.query_params.get('price_gt', None)
  7.         # 判断前端是否传入过滤条件
  8.         if filter_comment:
  9.             # 根据条件进行赛选内容
  10.             books_queryset_filter = queryset.filter(price__gt=100)
  11.             # 返回过滤后的数据
  12.             return books_queryset_filter
  13.         return queryset
复制代码
视图类代码
  1. class BookView(ModelViewSet):
  2.     queryset = Book.objects.all()
  3.     serializer_class = BookSerializer
  4.     # 第三方过滤插件
  5.     filter_backends = [CommonFilter]
复制代码
五、排序的使用

用法简介

  • 排序需要和自定义过滤继承同一个父类,需要将排序的对象填入在过滤的列表内,并且放在其他参数的前方
    模块地址:
  1. from rest_framework.filters import OrderingFilter
复制代码
(2)、代码用法

视图类代码
  1. from rest_framework.viewsets import ModelViewSet
  2. from .models import Book
  3. from .serializer import BookSerializer
  4. from django_filters.rest_framework import DjangoFilterBackend
  5. from .filters import CommonFilter
  6. from rest_framework.filters import OrderingFilter
  7. class BookView(ModelViewSet):
  8.     queryset = Book.objects.all()
  9.     serializer_class = BookSerializer
  10.     # 第三方过滤插件(OrderingFilter放在其他过滤参数前)
  11.     filter_backends = [OrderingFilter, DjangoFilterBackend, CommonFilter]
  12.     # 查询的字段
  13.     filterset_fields = ['id', 'name', 'price']
  14.     # 指定排序的字段(-是降序,默认升序)
  15.     ordering_fields = ['price']
复制代码
搜索用法
  1. # 默认升序
  2.     http://127.0.0.1:8000/api/books/?price_gt=60&ordering=price
  3.    
  4. # 降序
  5.         http://127.0.0.1:8000/api/books/?price_gt=60&ordering=-price
复制代码
六、分页


  • 分页功能,只有查询所有接口,才有分页
  • drf内置了三个分页器,对应三种分页方式
  • 内置的分页类不能直接使用,需要继承,定制一些参数后才能使用
使用步骤

  • 步骤一:创建一个py文件编写分页用到的自定义类,分页的三个类并不能直接使用,需要我们进行配置
  • 步骤二:编写这个自定义类
  • 步骤三:导入视图类中,并添加配置
代码
page.py(自定义的分页类)
  1. from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
  2. # 网页用它
  3. class CommonPageNumberPagination(PageNumberPagination):
  4.     page_size = 2  # 每页显示2条
  5.     page_query_param = 'page'  # page=10  查询第10页的数据,每页显示2条
  6.     page_size_query_param = 'size'  # page=10&size=5    查询第10页,每页显示5条
  7.     max_page_size = 5  # 每页最大显示10条
  8. '''
  9. page_size 每页数目
  10. page_query_param 前端发送的页数关键字名,默认为”page”
  11. page_size_query_param 前端发送的每页数目关键字名,默认为None
  12. max_page_size 前端最多能设置的每页数量
  13. '''
  14.    
  15.    
  16. # LimitOffset
  17. class CommonLimitOffsetPagination(LimitOffsetPagination):
  18.     default_limit = 3  # 每页显示2条
  19.     limit_query_param = 'limit'  # limit=3   取3条
  20.     offset_query_param = 'offset'  # offset=1  从第一个位置开始,取limit条
  21.     max_limit = 5
  22.     # offset=3&limit=2      0  1 2 3 4 5
  23. '''
  24. default_limit 默认限制,默认值与PAGE_SIZE设置一直
  25. limit_query_param limit参数名,默认’limit’
  26. offset_query_param offset参数名,默认’offset’
  27. max_limit 最大limit限制,默认None
  28. '''
  29. # app 用下面
  30. class CommonCursorPagination(CursorPagination):
  31.     cursor_query_param = 'cursor'  # 查询参数
  32.     page_size = 2  # 每页多少条
  33.     ordering = 'id'  # 排序字段
  34. '''
  35. cursor_query_param:默认查询字段,不需要修改
  36. page_size:每页数目
  37. ordering:按什么排序,需要指定
  38. '''
复制代码
views.py
  1. #  分页功能   必须是继承GenericAPIView ,如果是继承APIView,要自己写(你写)
  2. from .page import CommonPageNumberPagination as PageNumberPagination
  3. from .page import CommonLimitOffsetPagination as LimitOffsetPagination
  4. from .page import CommonCursorPagination as CommonCursorPagination
  5. class BookView(ViewSetMixin, ListAPIView):
  6.     queryset = Book.objects.all()
  7.     serializer_class = BookSerializer
  8.     permission_classes = []
  9.     authentication_classes = []
  10.     throttle_classes = []
  11.     # 之前的东西一样用 ,内置的分页类不能直接使用,需要继承,定制一些参数后才能使用
  12.     # pagination_class = PageNumberPagination
  13.     #基本分页方式(基本是这种,网页端):http://127.0.0.1:8000/api/v1/books/?page=2&size=3
  14.     # pagination_class = LimitOffsetPagination
  15.     # 偏移分页 http://127.0.0.1:8000/api/v1/books/?limit=4&offset=1
  16.     # 从第一条开始,取4条
  17.     pagination_class = CommonCursorPagination
  18.     # 游标分页,只能下一页,上一页,不能跳到中间,但它的效率最高,大数据量分页,使用这种较好
复制代码
来源:https://www.cnblogs.com/oiqwyig/p/17100179.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具