drf06

内容概要

  • 9个视图子类
  • 视图集
  • 路由系统
  • 认证组件

9视图子类

两个视图基类

5个视图扩展类

9个视图子类----》视图类,不需要各位继承

GenericAPIView,只需要继承9个中其中某个,就会有某个或多个接口

五个视图扩展类(不能单独使用)

#                                  查所有 只有list方法         添加 只有 create方法
from rest_framework.mixins import ListModelMixin, CreateModelMixin
#                                 查一个只有retrieve方法   修改 update方法    删除  destroy方法
from rest_framework.mixins import RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin

image-20230206184619442

九个视图子类

#                           查所有 get 调用 list   post调用 create    请两个结合
from rest_framework.generics import ListAPIView, CreateAPIView, ListCreateAPIView
#                          查一个  get 调用 retrieve   修改put 调用 update  删除 delete 调用 destroy
from rest_framework.generics import RetrieveAPIView, UpdateAPIView, DestroyAPIView
from rest_framework.generics import RetrieveUpdateAPIView, RetrieveDestroyAPIView, RetrieveUpdateDestroyAPIView

image-20230206184941523

image-20230206185117334

视图集

通过ModelViewSet编写5个接口

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'}))

]

image-20230206192229801

from rest_framework.viewsets import ModelViewSet

from .models import Book
from .serializer import BookSerializer


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

image-20230206192245473

serializer层

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['name', 'price', 'publish', 'authors',
                  'publish_dict', 'authors_list']
        extra_kwargs = {
            'publish': {"write_only": True},
            'authors': {"write_only": True},
        }

    publish_dict = serializers.DictField(read_only=True)
    authors_list = serializers.ListField(read_only=True)

image-20230206192307591

通过ReadOnlyModelView编写2个只读的接口

path('books/', views.BookView.as_view({'get': 'list', })),
path('books/<int:pk>/', views.BookView.as_view({'get': 'retrieve', }))

只能写有的没有的写报错

view

from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
class BookView(ReadOnlyModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

ViewSetMixin源码分析

image-20230206192728214

魔法方法不限制于函数,还有可能是类

image-20230206193445336

image-20230206193131351

路由匹配成功走ViewSetMixin的as_view,修改请求于函数的对应关系,然后去除django原生的csrf校验

总结

  1. 只要继承了ViewSetMixin的视图类,路由写法就变了(ViewSetMixin重写了as_view)

  2. 变成需要传入字典:{"get": "list"}

    只要传入actions,以后访问get就是访问list

  3. 其他执行跟之前一样

  4. 以后视图类,类中的方法名,可以任意命名,只要在路由中做好映射即可【重要】

from rest_framework.viewsets包下的类

#                    魔法,继承了修改路由的写法 一定要写继承父类的第一个
from rest_framework.viewsets import ViewSetMixin
#                     APIView+ViewSetMixin   GenericAPIView + ViewSet
from rest_framework.viewsets import ViewSet, GenericViewSet
#                   
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet

image-20230206194640379

重点

以后,你想继承APIView,但是想修改路由写法[视图类中的自定义命名],可以继承ViewSet。

以后,需要跟数据库打交道,想修改路由写法可以继承GenericViewSet。

视图层大总结

  1. 两个视图基类

    APIView,GenericAPIView

  2. 5个视图扩展类,不是视图类,必须配合GenericAPIView

  3. 9个视图子类,是视图类,只需继承其中一个就可以了

  4. 视图集

    -ModelViewSet:路由写法变了,只需写两行,5个接 口都有了

    -ReadOnlyModelViewSet:路由写法变了,只需要写 两行,2个只读接口都有了

    -ViewSetMixin:不是视图类,魔法,重写了as_view,路由写法发送改变,变成了映射

    views.BookView.as_view({'get': 'list', 'post': 'create'})

    -ViewSet:ViewSetMixin + APIView

    -GenericViewSet : GenericAPIView + ViewSetMixin

    ModelViewSet::五个扩展类+GenericViewSet

路由系统

def 由于继承ViewSetMixin类,路由写法变了

原生 + drf,以后的路由写法,可能会有如下情况(三种情况)

1. path("books/",view.BookView.as_view())
2. path("books/",view.BookView.as_view({"get": "list"}))

3. 自动生成

drf提供了两个路由类,继承ModelViewSet后,路由可以自动生成

使用步骤:

  1. 导入路由类

    from rest_framework.routers import SimpleRouter, DefaultRouter
    
  2. 生成实例化对象

    router = SimpleRouter() 或 router = DefaultRouter()
    
  3. 注册

    第一个参数是路由 不要加/
    第二个参数是视图类
    第三个参数是别名,一般和路由相同
    router.register('books', views.BookView, 'books') 
    
  4. 在urlpatterns中注册,两种方式

    1.  urlpatterns += router.urls
      
    2.  path('api/v1/', include(router.urls))
      

image-20230206205806553

image-20230206205737838

底层实现:自动生成路由

-本质就是自动做映射,能够自动生成的前提是,视图类中要有 5个方法的某一个或多个

get --------> list
post --------> create
get --------> retrieve
put --------> update
delete -------> destory

ModelViewSet,ReadOnlyModelViewSet可以自动生成

9个视图子类 + 配合ViewSetMixin 才可以自动生成

GenericAPIView + 5个视图扩展类 + 配合ViewSetMixin 才能自动生成

actionc装饰器

actionc 写在视图类的方法上,可以自动生成

url层

from rest_framework.routers import SimpleRouter, DefaultRouter


router = SimpleRouter()  

router.register('test', views.SendView, 'test')
urlpatterns = [
    path('admin/', admin.site.urls),
]
urlpatterns += router.urls

view

from rest_framework.viewsets import ViewSet
from rest_framework.response import Response

from rest_framework.decorators import action


class SendView(ViewSet):
    # 请求方式        
    #是否需要pk     
    #会在路由后在拼接一个 url_path 可以不写
	 # url_name:别名,反向解析使用,了解即可
    @action(methods=['POST', ], detail=False, url_path='send')
    def send(self, request):
        print(request.query_params)
        return Response("我是send")

image-20230206214027204

补充

SimpleRouter和DefaultRouter就是一个路由多一个路由少。我们一般用SimpleRouter,因为少,虽然少但是跟DefaultRouter功能实现的差不多

  1. 不同的请求方式可以使用不同序列化类

  2. 不同action使用不同序列化类

    class SendView(ViewSet):
        def get_serializer(self):
            #  self.action 拿到的 方法的名字
            if self.action == "lqz":
                return LqzSerializer
            elif self.action == "json":
                return JsonSerializer
            elif self.action == "send":
                return BookSerializer
    

认证组件

访问某个接口,需要登录后才能访问

第一步:登录功能,和用户表

User表

UserToken表:存储用户登录状态

登录接口

#### 表模型

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



class UserToken(models.Model):  # 跟User是一对一
    token = models.CharField(max_length=32)
    user = models.OneToOneField(to='User', on_delete=models.CASCADE, null=True)
    # user :反向,表名小写,所有有user字段

### 路由
router.register('user', views.UserView, 'user')  # /api/v1/user/login     post 请求

# 视图类
####  登录接口  自动生成路由+由于登录功能,不用序列化,继承ViewSet
from .models import User, UserToken
import uuid


class UserView(ViewSet):
    @action(methods=['POST'], detail=False)
    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:
            # 用户存在,登录成功
            # 生成一个随机字符串--uuid
            token = str(uuid.uuid4())  # 生成一个永不重复的随机字符串
            # 在userToken表中存储一下:1 从来没有登录过,插入一条,     2 登录过,修改记录
            # 如果有就修改,如果没有就新增  (if 自己写)
            # kwargs 传入的东西查找,能找到,使用defaults的更新,否则新增一条
            UserToken.objects.update_or_create(user=user, defaults={'token': token})
            return Response({'code': '100', 'msg': '登录成功', 'token': token})
        else:
            return Response({'code': '101', 'msg': '用户名或密码错误'})

作业

# 1 整理出视图层所有的类,以及继承关系,最好画图,每个视图类,扩展类,如何使用,也要简单写写
# 2 整理自动生成路由和装饰器
# 3 登录接口写完(if,我写的方式)


---扩展
# 3 登录认证功能,没写,登录功能写完了
	-实现了图书5个接口
    -图书的查询所有和新增不需要登录访问
    -图书的查询一个,修改,删除,登录访问

view层

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

import hashlib


def get_md5(data):
    md5 = hashlib.md5()
    md5.update("宝塔镇河妖".encode("utf8"))
    md5.update(str(data).encode("utf8"))
    md5.update("天王盖地虎".encode("utf8"))
    return md5.hexdigest()


def outer(func):
    def inner(request, *args, **kwargs):
        token = request.session.get("token")
        print(token)
        if token:
            user_id = UserToken.objects.filter(token=token).first().user_id
            request.user_id = user_id
            res = func(request, *args, **kwargs)
            return res
        else:
            return Response({"msg": "滚蛋"})

    return inner


from django.utils.decorators import method_decorator
from .serializer import BookSerializer
from rest_framework.viewsets import ModelViewSet


class UserView(ViewSet):
    @action(methods=['POST', ], detail=False)
    def login(self, request):
        username = request.data.get("username")
        password = request.data.get("password")
        res = User.objects.filter(username=username, password=password).first()
        if res:
            # 生成一个独一无二
            token = uuid.uuid4()
            request.session['token'] = "%s|%s" % (str(token), str(get_md5(res.pk)))
            UserToken.objects.update_or_create(user=res, defaults={"token": request.session["token"]})
            return Response({'code': 100, 'msg': "登录成功"})
        else:
            return Response({'code': 101, 'msg': "用户名或密码错误"})

    @method_decorator(outer)
    @action(methods=["DELETE", ], detail=False)
    def logout(self, request):
        request.session.delete()
        return Response()


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

    @method_decorator(outer)
    @action(methods=['POST'], detail=False)
    def create_book(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

    @method_decorator(outer)
    @action(methods=['PUT'], detail=True)
    def update_book(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    @method_decorator(outer)
    @action(methods=['DELETE'], detail=True)
    def delete_book(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

serializer层

from rest_framework import serializers
from .models import Book


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['name', 'price', 'publish', 'authors',
                  'publish_dict', 'authors_list']
        extra_kwargs = {
            'publish': {"write_only": True},
            'authors': {"write_only": True},
        }

    publish_dict = serializers.DictField(read_only=True)
    authors_list = serializers.ListField(read_only=True)

model层

from django.db import models


class Book(models.Model):
    name = models.CharField(max_length=32, verbose_name="书名")
    price = models.CharField(max_length=12, verbose_name="价格")
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    @property
    def publish_dict(self):
        return {"name": self.publish.name, "addr": self.publish.addr}

    @property
    def authors_list(self):
        l = [{"name": author_obj.name, "gender": author_obj.gender.get_gender_display()} for author_obj in
             self.authors.all()]
        return l

    class Meta:
        verbose_name_plural = '图书表'

    def __str__(self):
        return "图书对象%s" % self.name


class Publish(models.Model):
    name = models.CharField(max_length=32, verbose_name='出版社名称')
    addr = models.CharField(max_length=12, verbose_name='出版社地址')

    class Meta:
        verbose_name_plural = '出版社表'

    def __str__(self):
        return '出版社对象%s' % self.name


class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name='作者名称')
    gender = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)

    class Meta:
        verbose_name_plural = '作者表'

    def __str__(self):
        return '作者对象%s' % self.name


class AuthorDetail(models.Model):
    choice_dict = ((1, '男'), (2, '女'))
    gender = models.IntegerField(choices=choice_dict, verbose_name='性别')

    class Meta:
        verbose_name_plural = '作者详情表'


class User(models.Model):
    username = models.CharField(max_length=32, verbose_name="用户名")
    password = models.CharField(max_length=32, verbose_name="密码")


class UserToken(models.Model):
    token = models.CharField(max_length=32, verbose_name="token")
    user = models.OneToOneField(to="User", on_delete=models.CASCADE)
posted @ 2023-02-06 23:49  可否  阅读(28)  评论(0)    收藏  举报