DRF之视图集

【 一 】视图集

​ 在 RESTful 架构中,对资源的常规操作无非就是查询、新增、修改、删除等这么几种。为此,django-rest-framework 分别提供了对应通用类视图函数。但是,如果对同一个资源的不同操作逻辑分散在各个视图函数中,从逻辑上来说不太合理,实际中管理起来也不是很方便,还会产生很多重复性的代码。因此,django-rest-framework 引入了视图集(Viewsets),把对同一个资源的不同操作,集中到一个类中。同样的,针对 Web 开发中的常见逻辑,django-rest-framework 也提供了通用视图集,进一步简化开发工作。

​ 使用视图集的一个更大的好处,就是可以配合 django-rest-framework 提供的路由器(router),自动生成 API 的 URL,不需要我们再手工将 URL 模式和视图函数绑定了。所以大部分情况下,即使对资源只有一种操作,我们一般也会使用视图集。

  • ModelViewSet # 5个接口—>路由写法:映射,自动生成
  • ReadOnlyModelViewSet, # 只读2个接口
  • ViewSet # ViewSetMixin+APIView
  • GenericViewSet # ViewSetMixin + GenericAPIView
  • ViewSetMixin # 路由写法变了

【1】使用方法

  • 人话就是可以简化我们的代码

  • 5个接口,都写—》通过一个视图类实现

# urls.py
from django.urls import path
from one.views import BookView 

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

]

# views.py
# 视图集 ModelViewSet
from rest_framework.viewsets import ModelViewSet
from one.models import Book
from one.serial import BookSerializer


class BookView(ModelViewSet):
    # 拿到所有的数据 (拿多条数据)
    # 查询所有和新增一条接口
    queryset = Book.objects.all()
    # 序列化类
    serializer_class = BookSerializer
    
    
# 序列化类

from rest_framework import serializers
from one.models import Book


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

【2】返回响应格式

class BookView(ModelViewSet):
    # 查询所有和新增一条接口
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    # 查询单个数据
    def retrieve(self, request, *args, **kwargs):
        res = super().retrieve(request, *args, **kwargs)
        print(res)
        # 从res中取出响应体内容---》res.data
        return Response({'code': 100, 'msg': '查询单条成功', 'result': res.data})

    # 子序列化版本
    def update(self, request, *args, **kwargs):
        # UpdateModelMixin(更改) update
        res = super().update(request, *args, **kwargs)
        # 从res中取出响应体内容---》res.data
        return Response({'code': 100, 'msg': '更改成功', 'result': res.data})

    def destroy(self, request, *args, **kwargs):
        # DestroyModelMixin(删除) destroy
        res = super().destroy(request, *args, **kwargs)
        # 从res中取出响应体内容---》res.data
        return Response({'code': 100, 'msg': '删除成功', 'result': res.data})

    def create(self, request, *args, **kwargs):
        # CreateModelMixin(添加) destroy
        res = super().create(request, *args, **kwargs)
        # 从res中取出响应体内容---》res.data
        return Response({'code': 100, 'msg': '成功', 'result': res.data})

当然视图集不会就是怎么简单

  • ModelViewSet:结合了 CRUD 操作的实现,适用于操作 Django ORM 模型。
  • ReadOnlyModelViewSet:只提供读取操作的视图集,不包括创建、更新和删除操作。
  • GenericViewSet:结合了 GenericAPIView 和 mixin 类,适用于定制化的视图逻辑,不依赖于特定的数据源。
  • ReadOnlyViewSet:提供只读操作的视图集,类似于 ReadOnlyModelViewSet,但不依赖于特定的数据源。
  • ViewSet:最基本的视图集,不包含任何 CRUD 操作的实现,通常用于定制化的视图逻辑。

【3】自动生成路由SimpleRouter

我这个要实现的功能是

# 1导入一个路由类
# 2 实例化得到对象
# 3 执行对象的方法
# 4 对象.属性 拿到值
# urls.py
from django.urls import path, include
from one.views import ReqsuetView, BookView,PublishView
# 自动生成路由
from rest_framework.routers import SimpleRouter
# 2 实例化得到对象
router=SimpleRouter()
# 3 执行对象的方法    BookView
router.register('books',BookView,'books')
# 4 对象.属性 拿到值
print(router.urls)
urlpatterns = [
    path('request/', ReqsuetView.as_view()),

]
# 5 把自动生成的路由,放到 urlpatterns中
urlpatterns+=router.urls
from rest_framework.viewsets import ModelViewSet
from one.models import Book
from one.serial import BookSerializer


class BookView(ModelViewSet):
    # 拿到所有的数据 (拿多条数据)
    # 查询所有和新增一条接口
    queryset = Book.objects.all()
    # 序列化类
    serializer_class = BookSerializer

【4】SimpleRouter和DefaultRouter的区别

SimpleRouterDefaultRouter 是 Django REST Framework 中两个常用的路由器类,它们用于自动生成 API URL 配置。它们之间的主要区别在于生成的 URL 配置和可用的操作。

  1. SimpleRouter

    • SimpleRouter 是一个简单的路由器,它为每个视图集提供默认的 URL 配置,包括列表视图、创建视图、详情视图、更新视图和删除视图。

    • 默认情况下,

      SimpleRouter
      

      会为每个视图集生成以下 URL 配置:

      • GET(列表)、POST(创建):/<resource>/
      • GET(详情)、PUT(更新)、DELETE(删除):/<resource>/<pk>/
    • 这种简单的 URL 配置适用于大多数基本的 CRUD(创建、读取、更新、删除)操作。

  2. DefaultRouter

    • DefaultRouter 提供了与 SimpleRouter 类似的默认 URL 配置,但还额外提供了一个根视图(Root View)和一个 API 登录视图(API Login View)。

    • 根视图是一个包含所有可用 API 路由链接的视图,它可以帮助用户发现 API 中可用的端点。

    • API 登录视图提供了一个标准的登录视图,用于用户身份验证和获取访问 API 的权限。

    • 默认情况下,

      DefaultRouter
      

      会为每个视图集生成与

      SimpleRouter
      

      相同的 URL 配置,另外还会生成以下两个 URL 配置:

      • 根视图:/
      • API 登录视图:/api-auth/

​ 总的来说,SimpleRouter 更加轻量级和简单,适用于简单的 API,而 DefaultRouter 提供了额外的功能,如根视图和 API 登录视图,适用于更复杂的 API 项目。选择使用哪个取决于你的项目需求以及是否需要这些额外的功能。

【5】DefaultRouter

  • # 使用action装饰器定制详细路由

# urls.py
from django.urls import path, include
from one.views import ReqsuetView, BookView,PublishView
# 自动生成路由
from rest_framework.routers import SimpleRouter
# 2 实例化得到对象
router=SimpleRouter()
# 3 执行对象的方法    BookView
router.register('books',BookView,'books')
# 4 对象.属性 拿到值
print(router.urls)
urlpatterns = [
    path('request/', ReqsuetView.as_view()),
	path('publish/', PublishView.as_view({'get': 'login'})),
]
# 5 把自动生成的路由,放到 urlpatterns中
urlpatterns+=router.urls

http://127.0.0.1:8080/one/publish/

from rest_framework.viewsets import ModelViewSet
from one.models import Book
from one.serial import BookSerializer


class BookView(ModelViewSet):
    # 拿到所有的数据 (拿多条数据)
    # 查询所有和新增一条接口
    # 数据记者: 所有要序列化的数据
    queryset = Book.objects.all()
    # 完成序列化
    serializer_class = BookSerializer



# 使用action装饰器定制详细路由
from rest_framework.decorators import action
from rest_framework.viewsets import ViewSetMixin
class PublishView(ViewSetMixin, GenericAPIView):
    # get请求执行它  http://127.0.0.1:8080/one/publish/
    @action(methods=['GET'], detail=False)
    def login(self, request):
        return Response('你妈死了')

image-20240415231541450

【6】登陆注册接口的实现

  • 序列化类

class SileBookSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id','name']


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['username','password']
        extra_kwargs = {
            # 确保密码只在创建时输入,而不是在序列化输出中显示
            'password':{'write_only':True}
        }

    def create(self,validated_data):
        user = User.objects.create(**validated_data)
        return user

    def update(self, instance, validated_data):
        instance.username = validated_data.get('username', instance.username)
        password = validated_data.get('password')
        if password:
            # 如果提供了新密码,则更新密码
            instance.set_password(password)
        instance.save()
        return instance
  • views.py

from rest_framework.decorators import action
from rest_framework.viewsets import ViewSet
from two.serial import BookSerializer, SileBookSerializer,UserSerializer
from rest_framework.response import Response
from two.models import Book2, User, UserToken
import uuid


class UsersViews(ViewSet):
    @action(methods=['GET'], detail=False)
 	# http://127.0.0.1:8200/two/users/login/
    def login(self, request):
        # 用户名、密码 ---> request.data
        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)
            print(token)
            # 登录成功 --> 生成随机字符串: token---> 存到UserToken表中
            return Response({'code': 200, 'msg': '登录成功', 'token': token})
        else:
            return Response({'code': 404, 'msg': '用户名或者密码错误!!!'})

    @action(methods=['POST'], detail=False)
    #http://127.0.0.1:8200/two/users/register/
    @action(methods=['POST','PUT'], detail=False)
    def register(self, request):
        serializer = UserSerializer(data=request.data)
        if serializer.is_valid():
            username = serializer.validated_data.get('username')
            if User.objects.filter(username=username).exists():
                return Response({'code': 403, 'msg': '用户已存在', 'res': serializer.data})
            else:
                serializer.save()
                return Response({'code': 200, 'msg': '注册成功', 'res': serializer.data})
        else:
            return Response({'code': 404, 'msg': '注册失败', 'res': serializer.errors})
  • urls.py

from two.views import BookView, PublishView, Userview ,UsersViews # ,BookDetailView
# 自动生成路由
from rest_framework.routers import SimpleRouter, DefaultRouter
from django.urls import path,include
# 2 实例化得到对象
# router = SimpleRouter()
router = DefaultRouter()
# 3 执行对象的方法
# 这个方法就是自动帮我们生成映射  {'get': 'list', 'post': 'create'}
router.register('user', Userview, 'user')

router.register('users', UsersViews, 'users')
# router.register('books', BookView, 'books')


# 4 对象.属性 拿到值
print(router.urls)

# [<URLPattern '^books/$' [name='books-list']>, <URLPattern '^books/(?P<pk>[^/.]+)/$' [name='books-detail']>]
urlpatterns = [
    # path('request/', ReqsuetView.as_view()),
    #  什么是映射下面这种就是
    path('publish/', PublishView.as_view({'get': 'login'})),

    # 前面可以再加前缀
    path('api/v1/',include(router.urls))
]
# 5 把自动生成的路由,放到 urlpatterns中
urlpatterns += router.urls

image-20240416210336845

image-20240416210551929

【7】修改密码接口

  • 这里有个前提条件了
  • 我这里要实现是密文校验的密码修改
  • 就需要继承超级管理员的表中的数据
准备工作
  • 序列化类

  • create_user这个就是我们要用到超级管理员表中数据进行加密的作用了
    
  •     def create(self,validated_data):
            # 加密密码
            user = Usersuper.objects.create_user(**validated_data)
            return user
    
from rest_framework import serializers
from one.models import Book
from .models import Book2,Usersuper,UserToken


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



class SileBookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Usersuper
        fields = ['id','name']


class UserSerializer(serializers.ModelSerializer):
    # 添加 mobile 字段
    mobile = serializers.CharField(required=False)
    class Meta:
        model = Usersuper
        fields = ['username','password','mobile']
        extra_kwargs = {
            # 确保密码只在创建时输入,而不是在序列化输出中显示
            'password':{'write_only':True}
        }

    def create(self,validated_data):
        # 加密密码
        user = Usersuper.objects.create_user(**validated_data)
        return user

class PutSerializer(serializers.Serializer):
    old_password = serializers.CharField(required=True)
    new_password = serializers.CharField(required=True)
  • models.py


from django.db import models
from django.contrib.auth.models import User, AbstractUser

class Usersuper(AbstractUser):
    '''
        如果继承了AbstractUser
        那么在执行数据库迁移命令的时候,auth_user表就不会被创建
        而 UserInfo 会在 auth_user表 的基础上添加自定义扩展的字段

        优点:
            直接通过自己定义的表快速完成操作及扩展

        前提
            (1)在执行之前没有执行过数据库迁移命令
                auth_user 表没有被创建
                如果当前库已经被创建,则需要更换新的库
            (2)继承的表里面不要覆盖 AbstractUser 里面的字段名
                表里面有的字段不要动,只扩展额外的字段即可
            (3)需要再配置文件中声明Django要使用 UserInfo 替代 auth_user
                AUTH_USER_MODEL = 'app01.UserInfo'  ---'应用名.表名'

        '''
    phone = models.CharField(max_length=32)

class Book2(models.Model):
    name = models.CharField(max_length=88)
    price = models.IntegerField()
    publish = models.CharField(max_length=66)




class UserToken(models.Model):
    token = models.CharField(max_length=255)
    # 一对一关系   UserToken是一 User是一
    user = models.OneToOneField(to=Usersuper,on_delete=models.CASCADE)

注意要在配置文件中设置

# AUTH_USER_MODEL = 'two.Usersuper'
应用软件.表名
  • urls.py

这里要导入

from django.contrib.auth.hashers import check_password,make_password
import uuid
'''
check_password(password, encoded):这个函数用于检查用户输入的密码是否与存储在数据库中的哈希密码匹配。它接受两个参数,第一个参数是用户输入的明文密码,第二个参数是存储在数据库中的密码的哈希值。如果密码匹配,返回 True,否则返回 False。

make_password(password, salt=None, hasher='default'):这个函数用于对密码进行哈希加密。它接受三个参数,第一个参数是要加密的密码明文,第二个参数是一个可选的盐值(默认为 None,Django 会自动生成盐值),第三个参数是哈希算法(默认为 'default',即使用 PBKDF2 算法)。函数返回一个哈希后的密码字符串。

'''
  • 登陆接口

class UsersViews(ViewSet):
    # permission_classes = [IsAuthenticated]
    @action(methods=['GET'], detail=False)
    def login(self, request):

        # 用户名、密码 ---> request.data
        username = request.data.get('username')
        password = request.data.get('password')

        user = Usersuper.objects.get(username=username)
        print(user)
        print(password)
        if check_password(password, user.password):  # 检查用户输入的密码是否与数据库中存储的密码匹配
            print(password)
            print(user.password)
            token = str(uuid.uuid4())
            UserToken.objects.update_or_create(defaults={'token': token}, user=user)
            print(token)
            # 登录成功 --> 生成随机字符串: token---> 存到UserToken表中
            return Response({'code': 200, 'msg': '登录成功', 'token': token})
        else:
            return Response({'code': 404, 'msg': '用户名或者密码错误!!!'})

  • 注册接口

    @action(methods=['POST', 'PUT'], detail=False)
    def register(self, request):
        serializer = UserSerializer(data=request.data)
        if serializer.is_valid():
            username = serializer.validated_data.get('username')
            print(username)
            if Usersuper.objects.filter(username=username).exists():
                return Response({'code': 403, 'msg': '用户已存在', 'res': serializer.data})
            else:
                serializer.save()
                return Response({'code': 200, 'msg': '注册成功', 'res': serializer.data})
        else:
            return Response({'code': 404, 'msg': '注册失败', 'res': serializer.errors})
  • make_password(加密的密码明文) 修改密码接口

    @action(methods=['PUT'], detail=False)
    def change_password(self, request, *args, **kwargs):
        username = request.data.get('username')
        old_password = request.data.get('old_password')
        new_password = request.data.get('new_password')
        print(username)
        # 验证用户身份
        user = Usersuper.objects.filter(username=username).first()
        if not user:
            return Response({'code': 404, 'msg': '用户不存在'})
        print(old_password)
        print(new_password)
        print(user.password)
        # 检查旧密码是否正确
        if not check_password(old_password, user.password):

            return Response({'code': 403, 'msg': '旧密码错误'})


        # 保存密码
        # make_password(加密的密码明文)
        user.password = make_password(new_password)
        user.save()

        return Response({'code': 200, 'msg': '密码已成功更新'})

image-20240416233359911

【 二 】视图结构的用法和继承关系

19

  • 2个视图基类,5个视图扩展类,9个视图子类和视图集

【1】视图基类:

  1. APIView:这是 Django REST Framework 中最基本的视图类之一,它提供了最基本的请求处理方法,并且不依赖于任何特定的数据源或模型。
  2. GenericAPIView:这是一个更高级的基类,结合了 APIView 和一些 mixin 类,提供了对常见 CRUD 操作的支持,通常用于与 Django ORM 或其他数据源交互。

【2】视图扩展类(Mixin 类):

  1. CreateModelMixin:提供创建资源的功能,通常用于 POST 请求。
  2. RetrieveModelMixin:提供检索单个资源的功能,通常用于 GET 请求。
  3. DestroyModelMixin:提供删除资源的功能,通常用于 DELETE 请求。
  4. ListModelMixin:提供列举资源列表的功能,通常用于 GET 请求。
  5. UpdateModelMixin:提供更新资源的功能,通常用于 PUT 或 PATCH 请求。

【3】视图子类:

  1. ListAPIView:继承自 GenericAPIView 和 ListModelMixin,用于展示列表数据。
  2. RetrieveAPIView:继承自 GenericAPIView 和 RetrieveModelMixin,用于检索单个数据。
  3. CreateAPIView:继承自 GenericAPIView 和 CreateModelMixin,用于创建新的数据。
  4. DestroyAPIView:继承自 GenericAPIView 和 DestroyModelMixin,用于删除数据。
  5. UpdateAPIView:继承自 GenericAPIView 和 UpdateModelMixin,用于更新数据。
  6. RetrieveUpdateAPIView:继承自 GenericAPIView、RetrieveModelMixin 和 UpdateModelMixin,用于检索和更新数据。
  7. RetrieveDestroyAPIView:继承自 GenericAPIView、RetrieveModelMixin 和 DestroyModelMixin,用于检索和删除数据。
  8. ListCreateAPIView:继承自 GenericAPIView、ListModelMixin 和 CreateModelMixin,用于列举和创建数据。
  9. RetrieveUpdateDestroyAPIView:继承自 GenericAPIView、RetrieveModelMixin、UpdateModelMixin 和 DestroyModelMixin,用于检索、更新和删除数据。

【4】视图集(ViewSets):

ModelViewSet:结合了 CRUD 操作的实现,适用于操作 Django ORM 模型。

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    '''
    提供默认 'create()', 'retrieve()', 'update()' 的视图集,
    'partial_update()'、'destroy()' 和 'list()' 操作
    '''
    pass

ReadOnlyModelViewSet*:只提供读取操作的视图集,不包括创建、更新和删除操作。

class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
                           mixins.ListModelMixin,
                           GenericViewSet):
    """
    A viewset that provides default `list()` and `retrieve()` actions.
    """
    pass

GenericViewSet:结合了 GenericAPIView 和 mixin 类,适用于定制化的视图逻辑,不依赖于特定的数据源。

class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    """
    The GenericViewSet class does not provide any actions by default,
    but does include the base set of generic view behavior, such as
    the `get_object` and `get_queryset` methods.
    """
    pass

ReadOnlyViewSet:提供只读操作的视图集,类似于 ReadOnlyModelViewSet,但不依赖于特定的数据源。


ViewSet:最基本的视图集,不包含任何 CRUD 操作的实现,通常用于定制化的视图逻辑。

class ViewSet(ViewSetMixin, views.APIView):
    """
    The base ViewSet class does not provide any actions by default.
    这里的意思就是ViewSet这里不提供任何的默认操作
    """
    pass

【5】继承关系:

  • APIViewGenericAPIView 是其他视图类的基类。
  • GenericAPIView 结合了 GenericAPIView 和 mixin 类,提供了更多功能。
  • 大多数视图子类都是通过组合 GenericAPIView 和 mixin 类来实现特定功能的。

【6】属性和方法:

  • queryset:表示要操作的数据集。
  • serializer_class:用于序列化和反序列化数据的序列化器类。
  • get_queryset():获取要操作的数据集。
  • get_serializer_class():获取要使用的序列化器类。
  • perform_create():执行创建资源的操作。
  • perform_update():执行更新资源的操作。
  • perform_destroy():执行删除资源的操作。
  • list():列举资源列表。
  • retrieve():检索单个资源。
  • create():创建新的资源。
  • destroy():删除资源。
  • update():更新资源。

接口实现:

编写视图的步骤通常包括定义视图类,设置相关属性(如 querysetserializer_class 等),并根据需求重写相应的方法(如 get_queryset()perform_create() 等)来实现对应的接口功能。例如:

from rest_framework.generics import ListAPIView
from .models import Book
from .serializers import BookSerializer

class BookListView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

​ 这个例子定义了一个 BookListView 类,继承自 ListAPIView,用于展示书籍列表数据。queryset 设置为查询所有书籍对象,serializer_class 设置为书籍序列化器。这样,视图就能够正确地展示书籍列表数据了。

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值