drf之路由类与认证类
一、自动生成路由
对于我们的视图类只要继承了ViewSetMixin及其子类 那么路由的写法就需要改变
path('books/', views.BookView.as_view({'get': 'list'}))
这个时候有人写了两个路由类 能够自动生成路由,这样我们就可以不用再自己写映射关系了
# 自动生成路由 from rest_framework.routers import SimpleRouter, DefaultRouter # 1.实例化路由对象 router = SimpleRouter() # 2.注册路由 router.register('books', views.BookView, 'books') urlpatterns = [] # 3.添加路由 urlpatterns += router.urls # 这个时候就会自动产生路由了 # DefaultRouter和SimpleRouter区别: DefaultRouter的用法和SimpleRouter的用法是一样的 就是DefaultRouter 会多出一个路径 就是根路径 # 添加路由还有一种添加方式 # 实例化路由对象 router = SimpleRouter() # 注册路由 router.register('books', views.BookView, 'books') urlpatterns = [ path('', include(router.urls)), # 添加路由 这样也是可以的 ]
1 总结
# 我们在写自动产生路由的时候 其实映射关系已经定死了 book/---》get ---》list book/---》post---》create book/1 ---》get---》retrieve book/1---》put----》update book/1---》delete---》destroy # 请求方式与类中的方法其实已经定好了 # 我们在写视图类的时候类中必须有一下方法 -list,create,retrieve,update,destroy方法之一 -就是五个试图扩展类之一+GenericAPIView 九个子类也可以,还有ModelViewSet
# 现在我们自动生成路由的时候 想在视图类中写其他方法名字该怎么自动产生路由呢? class BookView(ModelViewSet): queryset = Book serializer_class = BookSerializer def login(self, request): pass # 该怎么让路由匹配成功之后 执行login方法呢? # 我们可以使用action装饰器 class BookView(ModelViewSet): queryset = Book serializer_class = BookSerializer @action(methods=['get'], detail=False, url_path='login', url_name='login') def login(self, request): pass # 这样写了之后 那么以后get请求都会执行该login方法
action参数解析
@action(methods=['get'], detail=False, url_path='login', url_name='login') def login(self, request): # methods 支持的请求方式,因为是列表,所以可以写多个 # detail 默认是False, 控制路由是生成 book/login 还是 book/pk/login False就生成book/login # url_path 控制book/后面的名字是什么 不写就是按照方法名 # url_name 起别名, 作用于反向解析
二、登入接口编写
# 我们先创建一个用户表先 class User(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=32) # 我们现在要想一想如何判断用户是否登入了? ''' 按照之前我们可以有session表可以记录登入用户的随机字符串, 但是我们现在前后端分离了 前端可以是浏览器、小程序、手机页面那么这个时候session就不能帮我们存储了 所以我们需要也需要一个东西帮我们来验证用户是否登入 我们可以在用户登入的时候 返回一个随机字符串 之后用户再登入的时候只需要携带这个字符串即可判断用户是否登入 所以我们还需要创建一个存储随机字符串的表 然后与用户表做一对一关系 ''' # 随机字符串表 class UserToken(models.Model): user = models.OneToOneField(to='User', on_delete=models.CASCADE) token = models.CharField(max_length=32) # 其实这个存储随机字符串的字段也可以存储到用户表中 只不过这样分开扩展性 还有就是 没有登入就为空 登录了就是有值 多次登入以最后一次为准
view.py
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 = uuid.uuid4() # 生成随机字符串 # 保存随机字符串 UserToken.objects.update_or_create(defaults={'token': token}, user=user) # update_or_create就是拿user判断UserToken中有没有值, # 如果没有值那么就用defaults中的数据跟user创建一个新的数据 # 如果有值,那么根据user找到该数据 然后根据defaults中的值 修改其中的值 return Response({'code': 100, 'msg': '登入成功', 'token': token}) # 把token返回给前端 之后用户登入的时候携带这个token登入即可 else: return Response({'code': 101, 'msg': '用户名或密码错误'})
urls.py
from rest_framework.routers import SimpleRouter, DefaultRouter router = SimpleRouter() router.register('user',views.UserView,'user') urlpatterns = [ path('admin/', admin.site.urls), path('', include(router.urls)), ]
三、认证
# 就是访问接口,通过认证才能够访问 # 现在我们写一个认证 让用户登入才能访问某个接口 # 认证类操作步骤: -1 编写一个认证类,然后继承BaseAuthentication -2 重新authenticate方法,在该方法下认证 -3 如果认证通过,返回两个值 -4 如果不通过,抛AuthenticationFailed异常 -5 只要返回两个值了,那么后续的request.user就是当前用户
具体操作
首先在app下创建一个auth.py,在该文件下写认证代码
from rest_framework.authentication import BaseAuthentication from .models import UserToken from rest_framework.exceptions import AuthenticationFailed class LoginAuth(BaseAuthentication): def authenticate(self, request): token = request.GET.get('token') # 前端携带的token是后端决定前端放哪携带过来的 我们决定token放在路径后面 user_token = UserToken.objects.filter(token=token).first() if user_token: return user_token.user, token # 返回两个值, 一个是当前登录用户,一个是token else: raise AuthenticationFailed('您没有登入')
view.py
class BookView(ModelViewSet): authentication_classes = [LoginAuth, ] queryset = Book serializer_class = BookSerializer # 这样用户在访问书籍接口的时候必须要登入才能够访问
这种配置属于局部配置,我们还可以使用全局配置,这样就可以不用在类中写authentication_classes属性了
# 在配置文件中下上这句代码即可全局配置 REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ['app01.auth.LoginAuth', ] } # 但是这样有点小问题 -那就是如果用户本来就没有登入 然后现在用户在访问登入接口的时候有让用户要先登入才能够访问 这样不就死循环了吗 -所以现在登入接口不需要这个认证类 那么就有了局部禁用 class UserView(ViewSet): authentication_classes = [] # 只需要在该属性的列表中不要写登入认证即可 @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 = uuid.uuid4() UserToken.objects.update_or_create(defaults={'token': token}, user=user) return Response({'code': 100, 'msg': '登入成功', 'token': token}) else: return Response({'code': 101, 'msg': '用户名或密码错误'})