一、路由Routers

在 Rest Framework 中提供了两个 router , 可以帮助我们快速的实现路的自动生成。

必须是继承 ModelViewSet 的视图类才能自动生成路由

SimpleRouter

使用方法:

urls.py

# 第一步:导入routers模块
from rest_framework import routers # 第二步:实例化得到对象
router = routers.SimpleRouter() # 第三步:注册( register('前缀', viewset视图集, 路由的别名) )
router.register('books', views.BooksViewset) # 第四步:生成路由加入到原路由中
# 方式一:
urlpatterns = [
...
]
urlpatterns += router.urls # 方式二:
urlpatterns = [
...
url(r'^', include(router.urls))
] # 形成路由如下
<URLPattern '^books/$' [name='books-list']>
<URLPattern '^books/(?P<pk>[^/.]+)/$' [name='books-detail']>

DefaultRouter

DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。

# 前两条和SimpleRouter一样
<URLPattern '^books/$' [name='books-list']>
<URLPattern '^books/(?P<pk>[^/.]+)/$' [name='books-detail']> # 效果也和前两条类似,
# 如:http://127.0.0.1:8000/books.json
<URLPattern '^books\.(?P<format>[a-z0-9]+)/?$' [name='books-list']>
# http://127.0.0.1:8000/books/1.json
<URLPattern '^books/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$' [name='books-detail']> # 多了个根路由http://127.0.0.1:8000/
<URLPattern '^$' [name='api-root']>, <URLPattern '^\.(?P<format>[a-z0-9]+)/?$' [name='api-root']>

action的使用

action是为了给继承自 ModelViewSet 的视图类中自定义的函数也添加路由

例如下面这样:

from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
from app01.ser import BooksSerializers
from app01.models import Books class BooksViewSet(ModelViewSet):
queryset = Books.objects.all()
serializer_class = BooksSerializers # 这种方法不会自动生成,需要用action配置
def get_num(self, request, pk):
book = self.get_queryset()[:int(pk)]
ser = self.get_serializer(book, many=True)
return Response(ser.data)

使用示例:

action是一个装饰器,放在被装饰的函数上方,

method:请求方式

detail:是否带pk ——>True 表示路径格式是xxx/<pk>/action方法名/——False 表示路径格式是xxx/action方法名/

from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
from rest_framework.decorators import action
from app01.ser import BooksSerializers
from app01.models import Books class BooksViewSet(ModelViewSet):
queryset = Books.objects.all()
serializer_class = BooksSerializers @action(methods=['GET', 'POST'], detail=True)
def get_num(self, request, pk):
book = self.get_queryset()[:int(pk)] # 获取前几条数据
ser = self.get_serializer(book, many=True)
return Response(ser.data) # 生成路由如下
http://127.0.0.1:8000/books/2/get_num/
<URLPattern '^books/(?P<pk>[^/.]+)/get_num/$' [name='books-get-num']>

二、认证

认证的写法

  1. 写一个认证类,继承 BaseAuthentication,重写 authenticate, 认证的逻辑写在里面,认证通过,返回两个值,一个值给Request对象的user, 认证失败,抛异常:APIException或者AuthenticationFailed
  2. 将认证类添加到需要认证视图类的authentication_classes = [认证类1]
  3. 全局使用,还是局部使用
# 全局使用,在setting.py中配置
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",]
} # 局部使用,在视图类上写
authentication_classes=[MyAuthentication]
# 局部禁用
authentication_classes=[]

认证源码分析

1、APIView重写as_view方法使之没有csrf认证——>但还是正常执行 dispatch 方法,但是 dispatch方法被 APIView重写了——>dispatch 中执行了 self.initial 认证方法——>有认证,权限,频率

2、现在只是看认证源码self.perform_authentication(request)

3、但是self.perform_authentication(request)就一句话:request.user,那么就需要去 drf 的 Request 对象中找 user 属性(方法)

@property
def user(self):
# 先去判断当前对象中有没有'_user'这个属性,一开始肯定是没有的,因为用户是没有登录的
if not hasattr(self, '_user'):
with wrap_attributeerrors():
# 没有用户,认证出用户
self._authenticate()
# 有用户,直接返回用户
return self._user

4、Request 类中的 user 方法,刚开始来,没有_user,走 self._authenticate()

5、核心,就是Request类中的 _authenticate(self)

def _authenticate(self):

    # 遍历拿到一个认证器,进行认证
# self.authenticators 配置的一堆认证类产生的认证类对象组成的 list
# self.authenticators 就是在视图类中配置的:authentication_classes = [认证类1,认证类2] 的一个个认证类的对象: ————>self.authenticators ==》 [认证类1对象,认证类2对象]
for authenticator in self.authenticators:
try: # 认证器调用认证方法authenticate(认证类对象self,request对象)
"""
def authenticate(self, request):
return (self.force_user, self.force_token)
"""
# 返回值:登录的用户与认证的信息组成的 tuple
# 并且该方法被try包裹,就代表该方法会抛异常,抛异常就代表认证失败
user_auth_tuple = authenticator.authenticate(self) # self是request对象
except exceptions.APIException:
self._not_authenticated()
raise # 返回值的处理
if user_auth_tuple is not None:
self._authenticator = authenticator
# 如果有返回值,就将 "登录用户" 与 "登录认证" 分别保存到 request.user / request.auth
self.user, self.auth = user_auth_tuple
return
# 如果返回值user_auth_tuple为空,代表认证通过,但是没有 "登录用户" 与 "登录认证信息",代表游客
self._not_authenticated()

认证组件的使用

1、写一个认证类,继承 BaseAuthentication,重写 authenticate

# app01_auth.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from app01.models import UserToken class TokenAuthentication(BaseAuthentication):
def authenticate(self, request):
# 认证逻辑,如果认证通过,返回两个值
# 如果认证失败,抛出AuthenticationFailed异常
token = request.data.get('token')
if token:
user_token = UserToken.objects.filter(token=token).first() # 认证通过
if user_token:
return UserToken.user, token
else:
raise AuthenticationFailed('认证失败')
else:
raise AuthenticationFailed('请求地址中需要带token')

2、将认证类添加到需要认证视图类的authentication_classes = [认证类1]

# views.py
from rest_framework.viewsets import ModelViewSet
from rest_framework.views import APIView
from rest_framework.response import Response
from app01.models import Books, UserInfo, UserToken
from app01.ser import BooksSerializer
from app01.app01_auth import TokenAuthentication
import uuid # 查看Books需要经过认证才能查看
class BooksView(ModelViewSet):
authentication_classes = [TokenAuthentication] queryset = Books.objects.all()
serializer_class = BooksSerializer # 登录视图,登录后获得token,后续用token认证
class LoginView(APIView): def post(self, request):
response_msg = {'status': 200, 'msg': ''}
username = request.data.get('username')
password = request.data.get('password')
user_obj = UserInfo.objects.filter(username=username, password=password).first() if user_obj:
# 登录成功生成一个随机字符串
token = uuid.uuid4() # 存到UserToken表中,update_or_create有就更新,没有就新增
UserToken.objects.update_or_create(defaults={'token': token}, user=user_obj)
response_msg['msg'] = '登录成功'
response_msg['token'] = token
else:
response_msg['msg'] = '账户或密码错误'
response_msg['status'] = 204
return Response(response_msg)

最新文章

  1. 【leetcode】Search in Rotated Sorted Array (hard)
  2. FZU 2150 Fire Game --两点同步搜索
  3. js call apply caller callee bind
  4. Python OpenCV ——Matplotlib显示图片
  5. html之ul标签
  6. Codeforces Beta Round #18 (Div. 2 Only) C. Stripe 前缀和
  7. 解决Linux/aix 下的websphere log4j不生效
  8. 深入JS系列学习2
  9. 旧版asp.net 发送邮件代码
  10. 在ubuntu10.0.4下更新git
  11. 代码的完整性:打印1到最大的n位数
  12. android DialogFragment 实现Dialog展示扫二维码图片展示
  13. File类_删除一个带内容的目录_练习
  14. How To Create A Local Repository For SUSE Linux
  15. &lt;![CDATA[]]&gt;和转义字符
  16. 51单片机数组的定义方法(code与data的作用)
  17. Hiho: 连通图
  18. vue 表单验证实例
  19. linxu信号种类
  20. ZOJ 3939 The Lucky Week (暴力找规律)

热门文章

  1. 自动做bond的脚本
  2. mysql基础之帮助信息
  3. Django中数据库操作相关的错误
  4. GO学习-(16) Go语言基础之文件操作
  5. node.js学习(3)模块
  6. Java设计模式(1:软件架构设计七大原则及开闭原则详解)
  7. Selenium click不生效 报错selenium.common.exceptions.InvalidArgumentException
  8. 基础BaseController
  9. Pptx的形状转为WPF的Geometry
  10. 自然语言推理:微调BERT