drf day 06

一、路由

  只要继承了ViewSetMixin及其子类的视图类,路由可以换一种写法

  1.1  自动生成路由

  1. 自动生成路由的写法

    -1、导入:from rest_framework.routers import SimpleRouter,DefaultRouter

    -2、实例化:router = SimpleRouter()

    -3、注册:router.register('user',views.UserView,'user')

    -4、加入到urlpatterns中(两种方式)

      - urlpatterns += router.urls

      - path(' ', include(router.urls)),

   2. 自动生成的路由映射关系其实定死了

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

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

       /books/1------->get------->retrieve

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

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

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

  4. SimpleRouter和DefaultRouter

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

代码展示:

ps:实例化产生对象(router = SimpleRouter()),要不要传参数问题:  【结果就是相当于没有参数】

ps:路由注册传参数问题:

ps:写好路由之后发送地址的结果

总体代码实现:

from django.contrib import admin
from django.urls import path, include   # include是干分发路由用的
from app01 import views

# 自动生成路由
# drf提供了两个路由类,以后继承了ViewSetMixin及其子类的视图类,就可以使用这两个路由类来自动生成路由,(也就是不用自己写了,让他自动去生成路由)
# 使用步骤
# 第一步:导入路由类
from rest_framework.routers import SimpleRouter, DefaultRouter

# 第二步:实例化得到对象
router = SimpleRouter()
# router = DefaultRouter()  就多一个根路径,和SimpleRouter几乎没差别,平时用的还是SimpleRouter多一些
# 第三步:注册路由(可以注册多个)
# 注册需要传三个参数  {第一个参数:路径 第二个参数:视图类(继承了ViewSetMixin及其子类的视图类) 第三个参数:别名,可以不写(默认以路径作为别名)}
router.register('books', views.BookView, 'books')
router.register('publish', views.PublishView, 'publish')

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

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

# 第四步:把自动生成的路由添加到urlpatterns中
# 方式一:
# urlpatterns+=router.urls

# 方式二,使用include添加   
# path('', include(router.urls)),

  1.2    action装饰器的使用【很重要

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

          methods:支持的请求方式,是一个列表形式(里面可以有多个请求方式)

       detail:默认是 False,控制生成的路由是 /user/login/ 还是/user/pk/login 【一般都是False,带pk的很少】

       url_path:控制生成的/user/后的路径是什么,如果不写,默认以方法名命名  /user/login/,  一般跟函数名同名即可

       url_name:别名,用于反向解析

        @action(methods=['GET','POST'],detail=True,url_path='login')
        def login(self,request,pk):

            ####这样写了以后可以自动生成路由

二、登录接口编写  

  2.1 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)  # 用户如果没有登录,就是空,如果登录了,就有值,登录多次以最后一次为准

  2.2  views.py   

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': '用户名或密码错误'})

  2.3  views.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. 如果认证通过,返回2个值

    4. 认证不通过抛AuthenticationFaild异常

    5. 只要返回了两个值,在后续的request.user 就是当前登录用户

      6. 如果想让某个视图类登录后才能访问

      -方式一:        

            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()) 
            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



class LoginAuth(BaseAuthentication):
    def authenticate(self, request):

        token = request.GET.get('token')
        user_token = UserToken.objects.filter(token=token).first()
        if user_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',]
        }

  3. 研究一下级联删除的其他()

    五种级联:

    (1)CASCADE:从父表中删除或更新记录时,同时自动的删除或更新子表中匹配的记录

    (2)SET NULL:从父表中删除或更新记录时,同时将子表的外键列设为空。

    (3)No ation:如果子表中有匹配的记录,则不允许对父表对应候选键进行修改和删除

    (4)Restrict:同no action,都是立即检查外键约束

    (5)Set default:父表有变更时,子表将外键设置成一个默认的值,但Innodb不能识别

posted @ 2022-10-08 16:45  W-Y-N  阅读(41)  评论(0)    收藏  举报