drf路由和三大认证之认证
自动生成路由
必须继承了ViewSetMixin以及子类的视图类才能自动生成路由
路由Routers
对于视图集ViewSet,我们除了可以自己动手指名请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由信息
rest_framework提供了两个router类
- SimpleRouter
- DefaultRouter
使用方法
- 导入
from rest_framework.routers import SimpleRouter,DefaultRouter
- 实例化
# router=DefaultRouter()
router=SimpleRouter()
- 注册
router.register('user',views.UserView,'user')
- 加入到
urlpatterns
中
方式1:urlpatterns+=router.urls
方式2:在urlpatterns添加:path('', include(router.urls))
注意:
自动生成的路由映射关系其实定死了
/books/ {'get':'list'}
/books/ {'post':'create'}
/books/1/ {'get':'retrieve'}
/books/1/ {'put':'update'}
/books/1/ {'delete':'destroy'}
所以,以后写的视图类不需要写action装饰器的话,视图类中必须要有(当然我们也可以通过action装饰器映射路由)
list、destory、retrieve、create、update方法之一
其实是必须是5个扩展类之一+GenericAPIView,或9个视图子类(ModelViewSet)
SimpleRouter和DefaultRouter的区别
DefaultRouter和SimpleRouter多一个根路径,显示所有注册过的路由
action装饰器的使用
使用场景
在视图集中,如果想要让Router自动帮助我们为自定义的动作生成路由信息,需要使用rest_framework.decorators.action
装饰器
以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同。
action装饰器方法的参数
- methods:支持的请求方式,列表或元素类型
- detail:默认是False,控制生成的路由是不是带pk
False: /user/login/
True:/user/pk/login/
- url_path:控制生成的/user/后的路径是什么,如果不写,默认以方法名命名
- url_name:别名,用于反向解析
案例
# views.py
class UserDetail(ViewSet):
authentication_classes = []
@action(methods=['GET',],detail=False)
def show(self,request):
return Response('show user_info')
# urls.py
from app01 import views
from rest_framework.routers import SimpleRouter,DefaultRouter
router=DefaultRouter()
router.register('user_detail',views.UserDetail,'user_detail')
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns+=router.urls
登录接口编写
models.py
# 用户表
class User(models.Model):
username=models.CharField(max_length=32)
password=models.CharField(max_length=32)
# 用户登录记录表
# 如何区分用户是否登录了
class UserToken(models.Model):
user=models.OneToOneField(to=User,on_delete=models.CASCADE)
token=models.CharField(max_length=50) # 用户如果没有登录,就是空,如果登录了,就有值,登录多次以最后一次为准
views.py
class UserView(ViewSet):
authentication_classes = []
@action(methods=['POST',],detail=False)
def login(self,request):
# 取出前端传入的用户名密码,校验,通过,返回登录成功,失败就返回用户名密码错误
username, password = request.data.get('username'), request.data.get('password')
user = User.objects.filter(username=username, password=password).first()
if user:
# 登录成功,不同人生成的token是不一样的,谁登录的,就把token存到UserToken表中
token = str(uuid4()) # 生成一个永不重复的随机字符串
# 存UserToken:如果没有记录,就是新增,如果有记录更新一下即可
# 通过user去UserToken表中查数据,如果能查到,使用defaults的数据更新,如果查不到直接通过user和defaults的数据新增
# defaults,更新或创建的数据
# kwargs,依据此查询
UserToken.objects.update_or_create(defaults={'token': token}, user=user)
return Response('登录成功')
else:
raise ValidationError('用户名或密码错误')
urls.py
from rest_framework.routers import SimpleRouter,DefaultRouter
router=DefaultRouter()
router.register('user',views.UserView,'user')
urlpatterns+=router.urls
认证
访问接口的时候,必须登录后才能访问。我们可以通过认证类完成这一需求
使用步骤
- 写一个认证类,继承BaseAuthentication
- 重写authenticate方法,在内部做认证
- 如果认证通过,返回2个值
- 认证不通过抛AuthenticationFailed异常
- 只要返回了两个值,在后续的
request.user
就是当前登录用户 - 如果先让某个视图类登录后才能访问
局部配置
class UserView(ViewSet):
authentication_classes = [LoginAuth,]
全局配置
REST_FRAMEWORK={
'DEFAULT_AUTHENTICATION_CLASSES': [
'app01.auth.LoginAuth'
]
}
局部禁用
authentication_classes = []
认证类
from rest_framework.authentication import BaseAuthentication
from .models import User,UserToken
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.exceptions import ValidationError
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
# 在这里做认证,校验用户是否登录(带了token,并且能查到,就是登录,返回两个值,否则就是没登录,抛异常)
# 用户带的token从哪取?后端人员定的:放在请求地址中
token=request.query_params.get('token')
# 通过token查询该token是否在表中有记录
user_token=UserToken.objects.filter(token=token).first()
if user_token:
return user_token.user,token 返回两个值,一个是当前登录用户,一个是token
else:
raise AuthenticationFailed('你尚未登录')
on_delete
1、models.CASCADE
级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除
2、models.SET_NULL
当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空
3、models.PROTECT
当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除
4、models.SET_DEFAULT
当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值
5、models.SET()
当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数
6、models.DO_NOTHING
什么都不做,一切都看数据库级别的约束,注数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似