django框架结构(django框架之drf(部分讲解))
一 、认证组件
简介:
登录认证的限制
认证组件是drf框架给我们提供的认证接口 ,它能够在请求进入视图函数/类前进验证(例如:认证用户是否登录) ,对不符合认证的请求进行拦截并返回校验失败的信息
(1) 、登录接口
# 认证是基于登录的接口上面操作的 所以前戏编写一个简单的登录接口 models.py class 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 相当于登录了之前的设备就无效了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): pass2.重写authenticate方法 ,在该方法在中实现登录认证
token在哪带的?如何认证它是登录了的?
用token来判断是否登陆 ,登陆了在访问的时候带上token ,目前阶段我们直接在地址栏中携带token的数据 ,后面可以在请求头中添加token的数据
3 、如果认证成功 ,返回两个值【返回None或两个值(固定的:当前登录用户 ,token)】
4 、认证不通过 ,用AuthenticationFailed类抛异常
代码如下:
authenticate.py(认证类) # 自己写的认证类,继承某个类 from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from .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 ,data5 、认证类的使用
当我们编写好了认证类中的认证代码,接着就需要导入到视图层然后使用他 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] # 需要写一个认证类 ,需要咱们自行编写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 、测试路由参考
(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 BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from .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 ,dataurls.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(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 ,如果没有权限 ,返回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: # 没有权限 ,向对象中放一个属性 message # 如果表模型中 ,使用了choice,就可以通过 get_字段名_display() 拿到choice对应的中文 self.message = 您是【%s】 ,您没有权限 % request.user.get_user_type_display() return Falseviews.py(视图类代码)
class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer # 局部认证 authentication_classes = [LoginAuth] # 权限认证(将编写的频率类导入过来) permission_classes = [CommentPermission]三 、频率组件
简介:
频率是指 ,控制某个接口访问频率(次数)(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, # 一分钟五次 }, }四 、过滤的多种用法
简介:
过滤是指在使用查询的时候 ,我们可以通过条件来过滤掉不需要的内容 # 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=西游记(3) 、使用第三方插件过滤(精准过滤)
第三方插件:
# 插件名称: django-filter # 安装插件: pip3.8 install django-filter模块地址:
from django_filters.rest_framework import DjangoFilterBackend代码用法:
from rest_framework.viewsets import ModelViewSet from .models import Book from .serializer import BookSerializer from 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=99 http://127.0.0.1:8000/api/books/?price=99&name=呐喊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 ModelViewSet from .models import Book from .serializer import BookSerializer from django_filters.rest_framework import DjangoFilterBackend from .filters import CommonFilter from 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 前端发送的每页数目关键字名 ,默认为None max_page_size 前端最多能设置的每页数量 # 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 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 PageNumberPagination from .page import CommonLimitOffsetPagination as LimitOffsetPagination from .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 # 游标分页 ,只能下一页 ,上一页 ,不能跳到中间 ,但它的效率最高,大数据量分页 ,使用这种较好创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!