drf06-自动生成路由-action-登录校验

自动生成路由

问题:路由中,每次我们写功能五个接口时,配置路由表都要配置如下两行。功能一多,就重复操作,冗余,造成问题。

path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'})),
path('books/int:pk', views.BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),

path('name/', views.NameView.as_view({'get': 'list', 'post': 'create'})),
path('name/int:pk', views.NameView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'})),
    path('books/<int:pk>', views.BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),

方案:根据功能注册,都自动生成路由。

自动生成路由

drf提供了两个路由类,以后继承了ViewSetMixin及其子类的视图类,就可以使用这两个路由类来自动生成路由

#思路
#1.导入DefaultRouter或者导入SimpleRouter
from rest_framework.routes import SimpleRoute,DefaultRouter

#2.实例化出router对象
route = SimpleRoute()

#3.路由注册(可以注册多个)
#把自动生成的路由添加到urlpatterns中
#第一个books 是浏览器访问的前缀想怎么写怎么写,为了好认,我写了books
#第二个views.BookView就是视图中你写的类,我自己写的BookView
#第三个别名,写不写都行,我写看了'books'
route.register('books', views.BookView,'books')

自动生成路由例子

你可看好咯

#模型层models.py

from django.db import models

# Create your models here.

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
    publish = models.CharField(max_length=32)

#视图层views.py

from django.shortcuts import render
# Create your views here.
from rest_framework.viewsets import ModelViewSet
from .models import Book
from .serializer import BookSerializer


class BookView(ModelViewSet):
    serializer_class = BookSerializer
    queryset = Book.objects.all()

项目中新建一个serializer.py文件

serializer.py

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

路由层urls.py

重要!!!自动生成路由的核心关键

四步走

from django.contrib import admin
from django.urls import path
from rest_framework import routers
from app01 import views

# 1.自动生成路由
# 导入SimpleRouter,DefaultRouter,两个随便导一个,一般用SimpleRouter
from rest_framework.routers import SimpleRouter, DefaultRouter
from django.urls import include
# 2.实例化
route = SimpleRouter()
# 3.注册路由(这里可以注册多个,我只写了一个book例子,所以只注册了一个),想注册多个,参考
#
route.register('zooks', views.BookView, 'book')

urlpatterns = [
    path('admin/', admin.site.urls),
    # path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'})),
    # path('books/<int:pk>', views.BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
 #4.配置添加路由信息
    path('', include(route.urls))
]

效果

image

自动生成路由的局限性

局限性:自动生成的路由映射关系其实写死了()

/books/---get---->list

/books/----post--->create

/books/---get -----retrieve

以后写的视图类,不写action装饰器的话,视图类中必须要有

list ,destroy,retrieve,create,update方法之一

其实是必须5个试图扩展类之一+GenericAPIView, 9个视图子类,ModelViewSet

action装饰器

在视图函数中,会有一些其它名字的方法,必须使用action装饰器做映射

def action(methods=None, detail=None, url_path=None, url_name=None, **kwargs):

#支持请求的方式,列表形式
methods: ['post']
-----------------------------------------------------------------------------
#默认是False,控制生成的路由是/user/login/还是/user/pk/login  是不是带什么
detail:
-------------------------------------------------------------------------------
#控制生成的/user/后的路径是什么,如果不写,默认以方法名 /user/login/ 一般跟函数名同名
url_path:
-----------------------------------------------------------------------------
#别名,用于反向解析
url_name:
from .models import Book
from .serializer import BookSerializer

class UserView(ViewSet):
@
    def login(self,request):
        return Response('ok')

action装饰器例子

urls.py

from django.contrib import admin
from django.urls import path

# 1.自动生成路由
# 导入SimpleRouter,DefaultRouter,两个随便导一个,一般用SimpleRouter
from rest_framework.routers import SimpleRouter,DefaultRouter
from django.urls import include
# 2.实例化
from app01 import views

router = SimpleRouter()
# 3.注册路由(这里可以注册多个,我只写了一个book例子,所以只注册了一个),想注册多个,参考
#
router.register('books', views.BookView, 'book')
router.register('user', views.UserView, 'user')

urlpatterns = [
    path('admin/', admin.site.urls),
 #4.配置添加路由信息
    path('', include(router.urls)),
]

views.py视图层

class UserView(ViewSet):
    @action(methods=['POST',], detail=False, url_path='login', url_name='login')
    def login(self, request):
        return Response('ok')

登录认证

登录接口编写

models,py

# 用户表
class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    def __str__(self):
        return self.username


# 用户登录记录表
# 如何区分用户是否登录了?
class UserToken(models.Model):
    # SET_NULL   SET_DEFAULT   CASCADE  SET(函数内存地址)
    user = models.OneToOneField(to='User', on_delete=models.CASCADE)
    token = models.CharField(max_length=32, null=True)  # 用户如果没有登录,就是空,如果登录了,就有值,登录多次以最后一次为准

views.py

from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet, ViewSet

from django.shortcuts import render

# Create your views here.
from .models import Book, User, UserToken
from .serializer import BookSerializer
import uuid
#--------------------------------------------------------------------------------------
class UserView(ViewSet):
    authentication_classes = []
    @action(methods=['POST', ], detail=False, url_path='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是不一样的,谁登录的,就把token存到UserToken表中
            token = str(uuid.uuid4())  # 生成一个永不重复的随机字符串
            # 存UserToken:如果没有记录,就是新增,如果有记录更新一下即可
            # 通过user去UserToken表中查数据,如果能查到,使用defaults的数据更新,如果查不到,直接通过user和defaults的数据新增
            UserToken.objects.update_or_create(defaults={'token': token}, user=user)
            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('user',views.UserView,'user')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(router.urls)),
]

3.认证

# 访问接口,必须登录后才能访问

# 通过认证类完成,使用步骤
	1 写一个认证类,继承BaseAuthentication
    2 重写authenticate方法,在内部做认证
    3 如果认证通过,返回2个值
    4 认证不通过抛AuthenticationFailed异常
    5 只要返回了两个值,在后续的request.user 就是当前登录用户
    
    
    6 如果想让某个视图类登录后才能访问
    	-方式一:
        	class BookView(ModelViewSet):
    			authentication_classes = [LoginAuth,]
        -方式二:全局配置
        	REST_FRAMEWORK={
            'DEFAULT_AUTHENTICATION_CLASSES':['app01.auth.LoginAuth',]
        }
            
         -局部禁用:
        	authentication_classes = []

3.1 认证类

from .models import UserToken
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed


class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # 在这里做认证,校验用户是否登录(带了token,并且能查到,就是登录,返回两个值,否则就是没登录,抛异常)
        # 用户带的token从哪取?后端人员定的:放在请求地址中
        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('您没有登录')

posted @ 2022-10-08 21:37  名字长的像一只老山羊  阅读(16)  评论(0编辑  收藏  举报