5

django框架之drf(部分讲解) - 吴仁耀

 1 year ago
source link: https://www.cnblogs.com/oiqwyig/p/17100179.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一、认证组件

  • 登录认证的限制

  • 认证组件是drf框架给我们提供的认证接口,它能够在请求进入视图函数/类前进验证(例如:认证用户是否登录),对不符合认证的请求进行拦截并返回校验失败的信息

(1)、登录接口

# 认证是基于登录的接口上面操作的 所以前戏编写一个简单的登录接口 models.pyclass User(models.Model): # 简易的用户信息账号密码 username = models.CharField(max_length=32) password = models.CharField(max_length=32) def __str__(self): return self.username '跟User表是一对一外键关联,存储用户登录状态用的 [这个表可以没有,如果没有,把字段直接写在User表上也可以]'class UserToken(models.Model): # 用户信息登录记录表 user = models.OneToOneField(to='User', on_delete=models.CASCADE) # 一对一关联 token = models.CharField(max_length=32, null=True) # 如果用户没有登录则没有值 如果登录则有值 views.py '登录接口功能:自动生成路由+登录功能,不用序列化,因此继承ViewSet即可'class UserView(ViewSet): @action(methods=['POST'], detail=False, url_path='login', url_name='login') 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: token = str(uuid.uuid4()) # uuid4 随机获得永不重复的字符串 机制跟Cookie中的验证码一样 # 在userToken表中存储一下:1 从来没有登录过,插入一条, 2 登录过,修改记录 UserToken.objects.update_or_create(defaults={'token': token}, user=user) # 通过user去UserToken表中查数据,如果能查到,使用defaults的数据更新,如果查不到,直接通过user和defaults的数据新增 # kwargs 传入的东西查找,能找到,使用defaults的更新,否则新增一条 return Response({'code': 100, 'msg': '登录成功', 'token': token}) else: return Response({'code': 101, 'msg': '用户名或密码错误'}) urls.py from rest_framework.routers import SimpleRouter, DefaultRouter router = SimpleRouter() router.register('users', views.UserView, 'users') urlpatterns += router.urls '''这个时候一个简单的登录接口就写好了 每次登录都会更新Token 相当于登录了之前的设备就无效了 '''
image

update_or_create源码如下:

def update_or_create(self, defaults=None, **kwargs): defaults = defaults or {} self._for_write = True with transaction.atomic(using=self.db): try: obj = self.select_for_update().get(**kwargs) except self.model.DoesNotExist: params = self._extract_model_params(defaults, **kwargs) obj, created = self._create_object_from_params(kwargs, params, lock=True) if created: return obj, created for k, v in defaults.items(): setattr(obj, k, v() if callable(v) else v) obj.save(using=self.db) return obj, False

(2)、认证组件使用步骤

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

  • 通过查看源码我们可以发现有个authenticate方法需要我们重写,否则就会报错,这就是我们需要编写认证功能的类
class BaseAuthentication: def authenticate(self, request): raise NotImplementedError(".authenticate() must be overridden.") def authenticate_header(self, request): pass

2.重写authenticate方法,在该方法在中实现登录认证

  • token在哪带的?如何认证它是登录了的?

  • token来判断是否登陆,登陆了在访问的时候带上token,目前阶段我们直接在地址栏中携带token的数据,后面可以在请求头中添加token的数据

3、如果认证成功,返回两个值【返回None或两个值(固定的:当前登录用户,token)】

4、认证不通过,用AuthenticationFailed类抛异常

代码如下:
authenticate.py(认证类)

# 自己写的认证类,继承某个类 from rest_framework.authentication import BaseAuthenticationfrom rest_framework.exceptions import AuthenticationFailedfrom .models import UserToken class LoginAuth(BaseAuthentication): def authenticate(self, request): # 在这里实现认证,如果是登录的,继续往后走返回两个值,如果不是抛异常 # 请求中是否携带token,判断是否登录,放在地址栏中 token = request.query_params.get('token', None) # 查找是否有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没传') # 前端传入的请求头中的数据从哪取? GET,body,POST,data

5、认证类的使用

  • 当我们编写好了认证类中的认证代码,接着就需要导入到视图层然后使用他
from rest_framework.generics import ListAPIView, RetrieveAPIViewfrom rest_framework.viewsets import ViewSetMixinfrom .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] # 需要写一个认证类,需要咱们自行编写

6、局部使用和全局使用

  • 局部使用:只在某个视图类中使用【当前视图类管理的所有接口】
class BookDetailView(ViewSetMixin, RetrieveAPIView): authentication_classes = [LoginAuth]
  • 全局使用:在配置文件settings.py中编写,全局所有接口都生效
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES':['app01.authenticate.LoginAuth'] }

注意事项:不要在配置文件中乱导入不使用的东西,否则会报错,但是在导入类似认证类这样的文件时,可以写上导入的代码然后再修改,最后写进配置中,这样可以减少错误

  • 局部禁用:(登陆接口很明显是不需要校验是否登陆的,因此有了这个局部禁用的需求,我们把他的authentication_classes配置成空就是局部禁用)
class BookDetailView(ViewSetMixin, RetrieveAPIView): authentication_classes = []

7、测试路由参考

image

(3)、整体代码

views.py

# 查询所有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 rest_framework.authentication import BaseAuthenticationfrom rest_framework.exceptions import AuthenticationFailedfrom .models import UserToken class LoginAuth(BaseAuthentication): def authenticate(self, request): # 在这里实现认证,如果是登录的,继续往后走返回两个值,如果不是抛异常 # 请求中是否携带token,判断是否登录,放在地址栏中 token = request.query_params.get('token', None) # 查找是否有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没传') # 前端传入的请求头中的数据从哪取? GET,body,POST,data

urls.py

from django.contrib import adminfrom django.urls import path, includefrom app01 import viewsfrom rest_framework.routers import SimpleRouter router = SimpleRouter() # 后面这个少的用的多,router.register('user', views.UserView, 'user')router.register('books', views.BookView, 'books')router.register('books', views.BookDetailView, 'books') urlpatterns = [ path('admin/', admin.site.urls), path('api/v1/', include(router.urls)), ]

简介:

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

(1)、权限组件的使用步骤

模块地址:

from rest_framework.permissions import BasePermission

用法简介:

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

(2)、代码用法

models.py(修改User表的配置后需要重新进行数据库迁移)

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

perssion.py(权限类代码)

# 写权限类,写一个类,继承基类BasePermission,重写has_permission方法,在方法中实现权限认证,如果有权限return True ,如果没有权限,返回Falsefrom 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: # 没有权限,向对象中放一个属性 message # 如果表模型中,使用了choice,就可以通过 get_字段名_display() 拿到choice对应的中文 self.message = '您是【%s】,您没有权限' % request.user.get_user_type_display() return False

views.py(视图类代码)

class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer # 局部认证 authentication_classes = [LoginAuth] # 权限认证(将编写的频率类导入过来) permission_classes = [CommentPermission]
image

三、频率组件

简介:

  • 频率是指,控制某个接口访问频率(次数)

(1)、频率组件的使用步骤

模块地址:

from rest_framework.throttling import BaseThrottle, SimpleRateThrottle# BaseThrottle:需要手动编写的代码较多# SimpleRateThrottle: 需要手动编写的代码较少(用这个)

用法简介

# 1、创建一个专门用来编写频率组件的py文件,写一个频率类,继承SimpleRateThrottle# 2、重写get_cache_key方法,返回什么,就以什么做限制----》ip(用户id做限制)# 3、配置一个类属性scope = 'book_5_m'# 4、在django的配置文件中编写频率次数 REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES': { 'book_5_m': '5/m', # 一分钟五次 }, }# 5、局部使用和全局使用 -局部使用: # 只影响当前的视图类 class BookView(ModelViewSet): throttle_classes = [CommentThrottle] -全局配置:影响全局 REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.CommonThrottle'], } -局部禁用: class BookView(ModelViewSet): throttle_classes = [CommentThrottle]

(2)、代码用法

throttle.py(频率类代码)

from rest_framework.throttling import BaseThrottle, SimpleRateThrottle class CommentThrottle(SimpleRateThrottle): # 创建一个用于控制频率的变量名(需要传入配置文件) scope = 'book_5_m' def get_cache_key(self, request, view): # 返回什么就以什么做限制(request.META.get('REMOTE_ADDR')以IP做限制) return request.META.get('REMOTE_ADDR')

视图类代码

class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer # 频率组件 throttle_classes = [CommentThrottle]

django配置文件

REST_FRAMEWORK = { # 控制访问频率 'DEFAULT_THROTTLE_RATES': { 'book_5_m': '5/m', # 一分钟五次 },}
image

四、过滤的多种用法

简介:

  • 过滤是指在使用查询的时候,我们可以通过条件来过滤掉不需要的内容
# restful规范中,要求了,请求地址中带过滤条件 -5个接口中,只有一个接口需要有过滤和排序,查询所有接口

(1)、继承APIView自己写

class BookView(APIView): def get(self,request): # 获取get请求携带的参数 name=request.query_params.get('name') # 通过filter进行过滤 books = Book.objects.filter(name=name)

(2)、使用drf的内置过滤(继承GenericAPIview)

模块地址:
该方法为模糊查询

from rest_framework.filters import SearchFilter

代码用法:

class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer # 实例化过滤对象 filter_backends = [SearchFilter] # 指定过滤的字段(模糊查询) search_fields = ['name', 'price']

搜索方式:

# name或price中只要有关键字就会搜出来 (只能用search=xxx的方式) http://127.0.0.1:8000/api/v1/books/?search=西游记
image

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

第三方插件:

# 插件名称: django-filter # 安装插件: pip3.8 install django-filter

模块地址:

from django_filters.rest_framework import DjangoFilterBackend

代码用法:

from rest_framework.viewsets import ModelViewSetfrom .models import Bookfrom .serializer import BookSerializerfrom django_filters.rest_framework import DjangoFilterBackend class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer # 第三方过滤插件 filter_backends = [DjangoFilterBackend] # 查询的字段 filterset_fields = ['pk','name', 'price']

搜索方式:

http://127.0.0.1:8000/api/books/?price=99http://127.0.0.1:8000/api/books/?price=99&name=呐喊
image

4、使用过滤组件

1.定制过滤组件的使用方式与步骤

模块地址:

from rest_framework.filters import BaseFilterBackend

用法简介:

# 1、创建一个专门用于过滤的py文件,写一个类继承BaseFilterBackend# 2、重写filter_queryset方法,在方法内部进行过滤# 3、直接返回过滤后的对象# 4、如果没有过滤直接返回所有数据# 5、局部使用 -局部使用: class BookView(ViewSetMixin, ListAPIView): queryset = Book.objects.all() serializer_class = BookSerializer # 在类表内填写过滤的类 filter_backends = [CommonFilter] # 可以定制多个,从左往右,依次执行

2.代码用法

过滤类代码filters.py

from rest_framework.filters import BaseFilterBackend class CommonFilter(BaseFilterBackend): # 重写的类,编写过滤吧的内容 def filter_queryset(self, request, queryset, view): # 获取过滤的条件 filter_comment = request.query_params.get('price_gt', None) # 判断前端是否传入过滤条件 if filter_comment: # 根据条件进行赛选内容 books_queryset_filter = queryset.filter(price__gt=100) # 返回过滤后的数据 return books_queryset_filter return queryset

视图类代码

class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer # 第三方过滤插件 filter_backends = [CommonFilter]

五、排序的使用

用法简介

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

(2)、代码用法

视图类代码

from rest_framework.viewsets import ModelViewSetfrom .models import Bookfrom .serializer import BookSerializerfrom django_filters.rest_framework import DjangoFilterBackendfrom .filters import CommonFilterfrom rest_framework.filters import OrderingFilter class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer # 第三方过滤插件(OrderingFilter放在其他过滤参数前) filter_backends = [OrderingFilter, DjangoFilterBackend, CommonFilter] # 查询的字段 filterset_fields = ['id', 'name', 'price'] # 指定排序的字段(-是降序,默认升序) ordering_fields = ['price']

搜索用法

# 默认升序 http://127.0.0.1:8000/api/books/?price_gt=60&ordering=price # 降序 http://127.0.0.1:8000/api/books/?price_gt=60&ordering=-price
  • 分页功能,只有查询所有接口,才有分页
  • drf内置了三个分页器,对应三种分页方式
  • 内置的分页类不能直接使用,需要继承,定制一些参数后才能使用

使用步骤

  • 步骤一:创建一个py文件编写分页用到的自定义类,分页的三个类并不能直接使用,需要我们进行配置
  • 步骤二:编写这个自定义类
  • 步骤三:导入视图类中,并添加配置

代码
page.py(自定义的分页类)

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, 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条 '''page_size 每页数目page_query_param 前端发送的页数关键字名,默认为”page”page_size_query_param 前端发送的每页数目关键字名,默认为Nonemax_page_size 前端最多能设置的每页数量''' # LimitOffsetclass 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'''default_limit 默认限制,默认值与PAGE_SIZE设置一直limit_query_param limit参数名,默认’limit’offset_query_param offset参数名,默认’offset’max_limit 最大limit限制,默认None''' # app 用下面 class CommonCursorPagination(CursorPagination): cursor_query_param = 'cursor' # 查询参数 page_size = 2 # 每页多少条 ordering = 'id' # 排序字段 '''cursor_query_param:默认查询字段,不需要修改page_size:每页数目ordering:按什么排序,需要指定'''

views.py

# 分页功能 必须是继承GenericAPIView ,如果是继承APIView,要自己写(你写)from .page import CommonPageNumberPagination as PageNumberPaginationfrom .page import CommonLimitOffsetPagination as LimitOffsetPaginationfrom .page import CommonCursorPagination as CommonCursorPagination class BookView(ViewSetMixin, ListAPIView): queryset = Book.objects.all() serializer_class = BookSerializer permission_classes = [] authentication_classes = [] throttle_classes = [] # 之前的东西一样用 ,内置的分页类不能直接使用,需要继承,定制一些参数后才能使用 # pagination_class = PageNumberPagination #基本分页方式(基本是这种,网页端):http://127.0.0.1:8000/api/v1/books/?page=2&size=3 # pagination_class = LimitOffsetPagination # 偏移分页 http://127.0.0.1:8000/api/v1/books/?limit=4&offset=1 # 从第一条开始,取4条 pagination_class = CommonCursorPagination # 游标分页,只能下一页,上一页,不能跳到中间,但它的效率最高,大数据量分页,使用这种较好

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK