首页 > 其他分享 >自动生成路由,action装饰器,登录接口,认证

自动生成路由,action装饰器,登录接口,认证

时间:2022-10-08 19:34:53浏览次数:45  
标签:models request 接口 token user action import class 路由

  • 自动生成路由

  • action装饰器的使用

  • 登录接口编写

  • 认证


自动生成路由

补充:

  1. 只要继承了ModelViewSet 就要在路由中as_view()内添加字典的形式

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

 

  2. 自动生成路由

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

自动生成路由步骤

urls.py

from django.contrib import admin
from django.urls import path,include
from app01 import views
# 1.导入类
from rest_framework.routers import SimpleRouter, DefaultRouter

# 2.实例化对象
router = SimpleRouter()
"""
  3.注册路由(可以注册多个)
    register里面有三个参数prefix, viewset, basename=None
        prefix          该视图集的路由前缀
        viewset         视图集
        basename=None   路由别名的前缀(就是给这个路由起别名,可以为空)
"""
router.register('book', views.BookView, )
router.register('publish', views.PublishView, )

urlpatterns = [
    path('admin/', admin.site.urls),
]
# 4. 添加路由
# 添加路由有两种方式
# 方式一
urlpatterns+=router.urls
"""
方式二
urlpatterns = [
    path('',include(router.urls))
]
"""

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 .models import Book
from .serializer import BookSerializer
from rest_framework.viewsets import ModelViewSet

# Create your views here.
from rest_framework import serializers


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

serializer.py

from rest_framework import serializers
from .models import Book

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

 

SimpleRouter和DefaultRouter

  -DefaultRouter比SimpleRouter多一个根路径,显示所有注册过的路由


 action装饰器使用

注意:继承了ViewSetMixin及其子类的视图类就可以自动生成路由,但是映射关系是定死的(get:list,post:create),自己想做映射关系需要使用装饰器(@action)

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

 

from rest_framework.decorators import action
from rest_framework.response import Response
class UserView(ViewSet):
    # action里面的参数methods = None, detail = None, url_path = None, url_name = None,
    """
    methods = None 数据提交方式列表的形式传入[’GET','POST',]可以是多个
    detail = None  默认是False 控制生成的路由是  /user/login/ 还是 /user/pk/login/  False的时候是没有pk值
    url_path = None 控制/user/后面的路径是什么,如果不写则以@action下面的方法名为准
    url_name = None 相当于起别名,用于反向解析
    """
    @action(methods=['GET', ], detail=False, url_path='login', url_name='login')
    def login(self, request,#pk):
        return Response('I love you!')

 还要在urls中注册

router.register('user',views.UserView,basename='user')

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

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

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

 


登录接口编写 

views.py

from .models import UserDetail,UserDetailToken
import uuid
from rest_framework.decorators import action
from rest_framework.response import Response

class UserDetailView(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 = UserDetail.objects.filter(username=username,password=password).first()
        if user:
            token = str(uuid.uuid4())  # 生成一个用不重复的随机字符串
            """
              update_or_create里面的参数  self, defaults = None, ** kwargs
                    defaults = defaults or {}
            """
            # 这句话的意思是如果校验过后user有值那么就更新token值,如果没有则新增一条user
            UserDetailToken.objects.update_or_create(defaults={'token':token},user=user)
            return Response({'code':100,'msg':'登录成功','token':token})
        else:
            return Response({'code': 101, 'msg': '用户名或密码错误'})

models.py

class UserDetail(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)

class UserDetailToken(models.Model):
    user = models.OneToOneField(to=UserDetail,on_delete=models.CASCADE)
    
    # 用户如果没有登录,就是空,如果登录了,就有值,登录多次以最后一次为准
    token = models.CharField(max_length=32,null=True)

   


认证

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

通过认证类完成,使用步骤
    1 写一个认证类,继承BaseAuthentication
    2 重写authenticate方法,在内部做认证
    3 如果认证通过,返回2个值
    4 认证不通过抛AuthenticationFailed异常
    5 只要返回了两个值,在后续的request.user 就是当前登录用户

auth.py

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('您没有登录')

 如果想让某个视图类登录后才能访问
        -方式一:
            class BookView(ModelViewSet):
         authentication_classes = [LoginAuth,]


        -方式二:全局配置
            REST_FRAMEWORK={
            'DEFAULT_AUTHENTICATION_CLASSES':['app01.auth.LoginAuth',]
        }

            
         -局部禁用:
            authentication_classes = []

 


作业

views.py

from rest_framework.viewsets import ViewSet
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, \
    ListModelMixin
from .models import Book, Publish
from .serializer import BookSerializer, PublishSerializer

from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView


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


class BookDetailView(RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer


class PublishView(GenericAPIView, ListModelMixin, CreateModelMixin):
    # 局部禁用
    authentication_classes = []

    queryset = Publish.objects.all()
    serializer_class = PublishSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class PublishDetailView(GenericAPIView, UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin):
    # 局部禁用
    authentication_classes = []

    queryset = Publish.objects.all()
    serializer_class = PublishSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)


from .models import User, UserToken
import uuid
from rest_framework.decorators import action
from rest_framework.response import Response


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())  # 生成一个用不重复的随机字符串
            """
              update_or_create里面的参数  self, defaults = None, ** kwargs
                    defaults = defaults or {}
            """
            # 这句话的意思是如果校验过后user有值那么就更新token值,如果没有则新增一条user
            UserToken.objects.update_or_create(defaults={'token': token}, user=user)
            return Response({'code': 100, 'msg': '登录成功', 'token': token})
        else:
            return Response({'code': 101, 'msg': '用户名或密码错误'})

models.py

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)


class Publish(models.Model):
    name = models.CharField(max_length=32)
    address = models.CharField(max_length=32)


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=32)

auth.py

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


# -1. 写一个认证类,继承BaseAuthentication
class LoginAuth(BaseAuthentication):
    # 0.重写authenticate
    def authenticate(self, request):
        # 1.在这里做认证,校验用户是否登录(带了token,并且能查到,就是登录,返回两个值,否则就是没登录,抛异常)
        token = request.GET.get('token')
        # 2.通过token查询该token是否是在表中有记录
        user_token = UserToken.objects.filter(token=token).first()
        if user_token:
            # 3.返回两个值,一个是当前登录用户,一个是token
            return user_token, token
        else:
            raise AuthenticationFailed('你还没有登录')

serializer.py

from .models import Book,Publish
from rest_framework import serializers

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

class PublishSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = '__all__'

urls.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')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/',views.BookView.as_view()),
    path('books/<int:pk>/',views.BookDetailView.as_view()),
    path('publishs/',views.PublishView.as_view()),
    path('publishs/<int:pk>/',views.PublishDetailView.as_view()),
    path('',include(router.urls))
]

settings.py

# 全局配置
REST_FRAMEWORK={
            'DEFAULT_AUTHENTICATION_CLASSES':['app01.auth.LoginAuth',]
        }

 

访问books不带token

访问books带token

访问publishs带不带token都行,因为有局部禁用:authentication_classes = []

                                         

on_delete = models.(参数)

CASCADE       删除关联数据的时候,与之的关联也删除

DO_NOTHING    删除关联数据的时候,什么操作也不做

PROTRCT       删除关联数据的时候,引发报错

SET_NULL      删除关联数据的时候,与之关联的值设置为空

SET_DEFAULT     删除关联数据的时候,与之关联的值设置为默认值

SET         删除关联数据(只要有关系的数据全部删除)

 

标签:models,request,接口,token,user,action,import,class,路由
From: https://www.cnblogs.com/scx-xiaochun/p/16769973.html

相关文章