drf框架 6 视图集与路由组件(开发最常用、最高级) 三大认证原理 RBAC认证规则

准备工作

models.py
from django.db import models

# 基类:是抽象的(不会完成数据库迁移),目的是提供共有字段的
class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)
    updated_time = models.DateTimeField(auto_now_add=True)

    class Meta:
        abstract = True  # 必须完成该配置

class Book(BaseModel):
    name = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=5, decimal_places=2, null=True)
    image = models.ImageField(upload_to=‘img‘, default=‘img/default.png‘)

    publish = models.ForeignKey(to=‘Publish‘, related_name=‘books‘, db_constraint=False, on_delete=models.DO_NOTHING)
    authors = models.ManyToManyField(to=‘Author‘, related_name=‘books‘, db_constraint=False)

    @property  # @property字段默认就是read_only,且不允许修改
    def publish_name(self):
        return self.publish.name

    @property  # 自定义序列化过程
    def author_list(self):
        temp_author_list = []
        for author in self.authors.all():
            author_dic = {
                "name": author.name
            }
            try:
                author_dic[‘phone‘] = author.detail.phone
            except:
                author_dic[‘phone‘] = ‘‘
            temp_author_list.append(author_dic)
        return temp_author_list

class Publish(BaseModel):
    name = models.CharField(max_length=64)

class Author(BaseModel):
    name = models.CharField(max_length=64)

class AuthorDetail(BaseModel):
    phone = models.CharField(max_length=11)
    author = models.OneToOneField(to=Author, related_name=‘detail‘, db_constraint=False, on_delete=models.CASCADE)
serializers.py
from rest_framework import serializers
from . import models

# 只有在资源需要提供群改,才需要定义ListSerializer,重写update方法
class BookListSerializer(serializers.ListSerializer):
    def update(self, queryset, validated_data_list):
        return [
            self.child.update(queryset[index], validated_data) for index, validated_data in enumerate(validated_data_list)
        ]

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        list_serializer_class = BookListSerializer
        model = models.Book
        fields = [‘name‘, ‘price‘, ‘image‘, ‘publish‘, ‘authors‘, ‘publish_name‘, ‘author_list‘]
        extra_kwargs = {
            ‘publish‘: {
                ‘write_only‘: True
            },
            ‘authors‘: {
                ‘write_only‘: True
            }
        }

基于GenericAPIView的十大接口

views.py
# 十大接口:
# 1)单查、群查、单增、单整体改、单局部改都可以直接使用
# 2)单删不能直接使用,因为默认提供的功能是删除数据库数据,不是我们自定义is_delete字段值修改
# 3)除了群查以为的接口,都要自己来实现

# 注:给序列化类context赋值{‘request‘: request},序列化类就可以自动补全后台图片链接
from rest_framework.generics import GenericAPIView
from rest_framework import mixins
from . import models, serializers
from rest_framework.response import Response

class BookV1APIView(GenericAPIView,
                    mixins.RetrieveModelMixin,
                    mixins.ListModelMixin,
                    mixins.CreateModelMixin,
                    mixins.UpdateModelMixin):

    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer

    def get(self, request, *args, **kwargs):
        if ‘pk‘ in kwargs:
            return self.retrieve(request, *args, **kwargs)  # 单查

        # queryset = models.Book.objects.filter(is_delete=False).all()
        # 注:给序列化类context赋值{‘request‘: request},序列化类就可以自动补全后台图片链接
        # serializer = serializers.BookModelSerializer(queryset, many=True, context={‘request‘: request})
        # return Response(serializer.data)
        return self.list(request, *args, **kwargs)  # 群查

    def post(self, request, *args, **kwargs):
        if not isinstance(request.data, list):
            return self.create(request, *args, **kwargs)

        serializer = self.get_serializer(data=request.data, many=True)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=201, headers=headers)

    def delete(self, request, *args, **kwargs):
        pk = kwargs.get(‘pk‘)
        if pk:
            pks = [pk]
        else:
            pks = request.data
        try:
            rows = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
        except:
            return Response(status=400)
        if rows:
            return Response(status=204)
        return Response(status=400)

    def put(self, request, *args, **kwargs):
        if ‘pk‘ in kwargs:
            return self.update(request, *args, **kwargs)

        pks = []
        try:
            for dic in request.data:
                pks.append(dic.pop(‘pk‘))
            objs = models.Book.objects.filter(is_delete=False, pk__in=pks)
            assert len(objs) == len(request.data)
        except:
            return Response(status=400)
        serializer = serializers.BookModelSerializer(instance=objs, data=request.data, many=True)
        serializer.is_valid(raise_exception=True)
        objs = serializer.save()
        return Response(serializers.BookModelSerializer(objs, many=True).data)

    def patch(self, request, *args, **kwargs):
        if ‘pk‘ in kwargs:
            return self.partial_update(request, *args, **kwargs)

        pks = []
        try:
            for dic in request.data:
                pks.append(dic.pop(‘pk‘))
            objs = models.Book.objects.filter(is_delete=False, pk__in=pks)
            assert len(objs) == len(request.data)
        except:
            return Response(status=400)
        serializer = serializers.BookModelSerializer(instance=objs, data=request.data, many=True, partial=True)
        serializer.is_valid(raise_exception=True)
        objs = serializer.save()
        return Response(serializers.BookModelSerializer(objs, many=True).data)

基于generics包下工具视图类的六大基础接口

views.py
# 六大基础接口
# 1)直接继承generics包下的工具视图类,可以完成六大基础接口
# 2)单查群查不能共存
# 3)单删一般会重写
from . import models, serializers
from rest_framework.response import Response
from rest_framework import generics
class BookV2APIView(generics.ListAPIView,
                    generics.RetrieveAPIView,
                    generics.CreateAPIView,
                    generics.UpdateAPIView,
                    generics.DestroyAPIView):
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer

    def get(self, request, *args, **kwargs):
        if ‘pk‘ in kwargs:
            return self.retrieve(request, *args, **kwargs)
        return self.list(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        pk = kwargs.get(‘pk‘)
        models.Book.objects.filter(is_delete=False, pk=pk).update(is_delete=True)
        return Response(status=204)

视图集

导入

""" ViewSetMixin类存在理由推到
1)工具视图类,可以完成应付六大基础接口,唯一缺点是单查与群查接口无法共存
    (只配置queryset、serializer_class、lookup_field)
2)不能共存的原因:RetrieveAPIView和ListAPIView都是get方法,不管带不带pk的get请求,只能映射给一个get方法
3)能不能修改映射关系:
    比如将/books/的get请求映射给list方法,
    将/books/(pk)/的get请求映射给retrieve方法,
    甚至可以随意自定义映射关系
"""

""" 继承视图集的视图类的as_view都是走ViewSetMixin类的,核心源码分析
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
    # ...

    # 没有actions,也就是调用as_view()没有传参,像as_view({‘get‘: ‘list‘})
    if not actions:
        raise TypeError("The `actions` argument must be provided when "
                        "calling `.as_view()` on a ViewSet. For example "
                        "`.as_view({‘get‘: ‘list‘})`")

        # ...

        # 请求来了走view函数
        def view(request, *args, **kwargs):
            # ...
            # 解析actions,修改 请求分发 - 响应函数 映射关系
            self.action_map = actions
            for method, action in actions.items():  # method:get | action:list
                handler = getattr(self, action)  # 从我们视图类用action:list去反射,所以handler是list函数,不是get函数
                setattr(self, method, handler)  # 将get请求对应list函数,所以在dispath分发请求时,会将get请求分发给list函数

                # ...
                # 通过视图类的dispatch完成最后的请求分发
                return self.dispatch(request, *args, **kwargs)

            # ...
            # 保存actions映射关系,以便后期使用
            view.actions = actions
            return csrf_exempt(view)
"""

核心

"""
视图集的使用总结
1)可以直接继承ModelViewSet,实现六大继承接口(是否重写destroy方法,或其他方法,根据需求决定)
2)可以直接继承ReadOnlyModelViewSet,实现只读需求(只有单查群查)
3)继承ViewSet类,与Model类关系不是很密切的接口:登录的post请求,是查询操作;短信验证码发生接口,借助第三方平台
4)继承GenericViewSet类,就代表要配合mixins包,自己完成任意组合
5)继承以上4个视图集任何一个,都可以与路由as_view({映射})配合,完成自定义请求响应方法
"""

案例

urls.py
url(r‘^v3/books/$‘, views.BookV3APIView.as_view(
    {‘get‘: ‘list‘, ‘post‘: ‘create‘, ‘delete‘: ‘multiple_destroy‘}
    )),

url(r‘^v3/books/(?P<pk>\d+)/$‘, views.BookV3APIView.as_view(
        {‘get‘: ‘retrieve‘, ‘put‘: ‘update‘, ‘patch‘: ‘partial_update‘, ‘delete‘: ‘destroy‘}
    )),
views.py
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
class BookV3APIView(ModelViewSet):
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer

    # 可以在urls.py中as_view({‘get‘: ‘my_list‘})自定义请求映射
    def my_list(self, request, *args, **kwargs):
        return Response(‘ok‘)

    # 需要完成字段删除,不是重写delete方法,而是重写destroy方法
    def destroy(self, request, *args, **kwargs):
        pk = kwargs.get(‘pk‘)
        models.Book.objects.filter(is_delete=False, pk=pk).update(is_delete=True)
        return Response(status=204)

    # 群删接口
    def multiple_destroy(self, request, *args, **kwargs):
        try:
            models.Book.objects.filter(is_delete=False, pk__in=request.data).update(is_delete=True)
        except:
            return Response(status=400)
        return Response(status=204)

路由组件:必须配合视图集使用

urls.py
from django.conf.urls import url, include
from . import views
# 路由组件,必须配合视图集使用
from rest_framework.routers import SimpleRouter
router = SimpleRouter()

# 以后就写视图集的注册即可:BookV3APIView和BookV4APIView都是视图集
router.register(‘v3/books‘, views.BookV3APIView, ‘book‘)
router.register(‘v4/books‘, views.BookV4APIView, ‘book‘)

urlpatterns = [
    url(‘‘, include(router.urls))
]
views.py
from rest_framework.viewsets import ReadOnlyModelViewSet
class BookV4APIView(ReadOnlyModelViewSet):
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer

自定义路由组件(了解)

router.py
from rest_framework.routers import SimpleRouter as DrfSimpleRouter
from rest_framework.routers import Route, DynamicRoute

class SimpleRouter(DrfSimpleRouter):
    routes = [
        # List route.
        Route(
            url=r‘^{prefix}{trailing_slash}$‘,
            mapping={
                ‘get‘: ‘list‘,
                ‘post‘: ‘create‘,  # 注:群增只能自己在视图类中重写create方法,完成区分
                ‘delete‘: ‘multiple_destroy‘,  # 新增:群删
                ‘put‘: ‘multiple_update‘,  # 新增:群整体改
                ‘patch‘: ‘multiple_partial_update‘  # 新增:群局部改
            },
            name=‘{basename}-list‘,
            detail=False,
            initkwargs={‘suffix‘: ‘List‘}
        ),
        # Dynamically generated list routes. Generated using
        # @action(detail=False) decorator on methods of the viewset.
        DynamicRoute(
            url=r‘^{prefix}/{url_path}{trailing_slash}$‘,
            name=‘{basename}-{url_name}‘,
            detail=False,
            initkwargs={}
        ),
        # Detail route.
        Route(
            url=r‘^{prefix}/{lookup}{trailing_slash}$‘,
            mapping={
                ‘get‘: ‘retrieve‘,
                ‘put‘: ‘update‘,
                ‘patch‘: ‘partial_update‘,
                ‘delete‘: ‘destroy‘
            },
            name=‘{basename}-detail‘,
            detail=True,
            initkwargs={‘suffix‘: ‘Instance‘}
        ),
        # Dynamically generated detail routes. Generated using
        # @action(detail=True) decorator on methods of the viewset.
        DynamicRoute(
            url=r‘^{prefix}/{lookup}/{url_path}{trailing_slash}$‘,
            name=‘{basename}-{url_name}‘,
            detail=True,
            initkwargs={}
        ),
    ]

上传文件接口

urls.py
from django.conf.urls import url, include
from . import views
# 路由组件,必须配合视图集使用
from rest_framework.routers import SimpleRouter
router = SimpleRouter()

# /books/image/(pk) 提交 form-data:用image携带图片
router.register(‘books/image‘, views.BookUpdateImageAPIView, ‘book‘)

urlpatterns = [
    url(‘‘, include(router.urls))
]
serializers.py
class BookUpdateImageModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = [‘image‘]
views.py
# 上次文件 - 修改头像 - 修改海报
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
class BookUpdateImageAPIView(GenericViewSet, mixins.UpdateModelMixin):
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookUpdateImageModelSerializer

权限

models.py
from django.db import models

# RBAC - Role-Based Access Control
# Django的 Auth组件 采用的认证规则就是RBAC

from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
    mobile = models.CharField(max_length=11, unique=True)

    def __str__(self):
        return self.username

class Book(models.Model):
    name = models.CharField(max_length=64)

    def __str__(self):
        return self.name

class Car(models.Model):
    name = models.CharField(max_length=64)

    def __str__(self):
        return self.name
settings.py
# 自定义User表,要配置
AUTH_USER_MODEL = ‘api.User‘
admin.py
from django.contrib import admin
from . import models

from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin

# 自定义User表后,admin界面管理User类
class UserAdmin(DjangoUserAdmin):
    # 添加用户课操作字段
    add_fieldsets = (
        (None, {
            ‘classes‘: (‘wide‘,),
            ‘fields‘: (‘username‘, ‘password1‘, ‘password2‘, ‘is_staff‘, ‘mobile‘, ‘groups‘, ‘user_permissions‘),
        }),
    )
    # 展示用户呈现的字段
    list_display = (‘username‘, ‘mobile‘, ‘is_staff‘, ‘is_active‘, ‘is_superuser‘)

admin.site.register(models.User, UserAdmin) # 加入UserAdmin,admin设置用户密码改为密文
admin.site.register(models.Book)
admin.site.register(models.Car)

导入

# 1)像专门做人员权限管理的系统(CRM系统)都是公司内部使用,所以数据量都在10w一下,一般效率要求也不是很高
# 2)用户量极大的常规项目,会分两种用户:前台用户(三大认证) 和 后台用户(BRAC来管理)
# 结论:没有特殊要求的Django项目可以直接采用Auth组件的权限六表,不需要自定义六个表,也不需要断开表关系,但可能需要自定义User表

做项目是否要分表管理前后台用户

"""
1)是否需要分表
答案:不需要
理由:前后台用户共存的项目,后台用户量都是很少;做人员管理的项目,基本上都是后台用户;前后台用户量都大的会分两个项目处理

2)用户权限六表是否需要断关联
答案:不需要
理由:前台用户占主导的项目,几乎需求只会和User一个表有关;后台用户占主导的项目,用户量不会太大

3)Django项目有没有必须自定义RBAC六表
答案:不需要
理由:auth组件功能十分强大且健全(验证密码,创建用户等各种功能);admin、xadmin、jwt、drf-jwt组件都是依赖auth组件的(自定义RBAC六表,插件都需要自定义,成本极高)
"""

权限六表:RBAC - Role-Based Access Control

三表

六表

三大认证规则

总结

# 1)后台用户对各表操作,是后台项目完成的,我们可以直接借助admin后台项目(Django自带的)
# 2)后期也可以用xadmin框架来做后台用户权限管理

# 3)前台用户的权限管理如何处理
#   定义了一堆数据接口的视图类,不同的登录用户是否能访问这些视图类,能就代表有权限,不能就代表无权限
#   前台用户权限用drf框架的 三大认证

# 注:前台用户权限会基于 jwt 认证

小结

"""
1)基于GenericAPIView类 加 mixins包 实现十大接口
class MyAPIView(GenericAPIView, mixins.RetrieveModelMixin, ...):
    # 配置queryset、serializer_class、lookup_field
    # 要自己定义get、post等方法,内部调用retrieve、create方法

2)基于 generics包 实现六大基础接口
class MyAPIView(generics.RetrieveAPIView, ...):
    # 配置queryset、serializer_class、lookup_field
    # 重写get处理单查群查共存即可
    # delete方法是否重写看需求

3)视图集(重点)
i)ViewSetMixin类:重写as_view方法
    作用:as_view({"get": "list"})来自定义请求与响应的映射关系

ii)两个视图集基类:
    ViewSet:与Model关系不是特别紧密
    GenericViewSet:与Model关系特别紧密

iii)两个GenericViewSet的子类:
    ModelViewSet:六大基础接口共存
    ReadOnlyModelViewSet:只读接口
    注:都只需要配置queryset、serializer_class、lookup_field;根据需求决定是否重写某些方法

iv)自己用两个视图集基类与mixins包形成自定义组合

4)上传图片:前台提交form-data,类型选择文件类型,后台用request.data和request.FILES都可以访问

5)路由:
from rest_framework.routers import SimpleRouter  (默认只提供了六大基础接口的配置)
router = SimpleRouter()
# 注册视图集
router.register(‘books‘, views.BookViewSet, ‘book‘)
urlpatterns = [
    url(‘‘, include(router.urls))
]

6)三大认证规则(后期分析)

7)RBAC认证六表

8)admin管理后台用户权限
    AdminUser辅助自定义User表,完成admin系统的注册

9)前台用户访问接口的权限交给 三大认证
"""

原文地址:https://www.cnblogs.com/ludingchao/p/12355357.html

时间: 2024-10-06 18:25:12

drf框架 6 视图集与路由组件(开发最常用、最高级) 三大认证原理 RBAC认证规则的相关文章

DRF框架之视图的扩展类简介

这里呢,我将为大家介绍一下DRF框架,为我们提供的试图扩展类的使用方法即作用. 在使用视图扩展类时,需要将mixins模块导入到view文件中. from rest_framework import mixins 并且,在使用视图扩展类时,必须结合GenericAPIView基类一起使用. 所谓,视图的扩展类,就是GenericAPIView的子类,他们继承自GenericAPIView类,并在此基础上封装了增删改查的功能函数. 模板代码: class BookInfoAPIView(mixin

django框架之视图系统和路由系统

内容回顾: 1. tags         1. for循环            {% for name in name_list %}                {{ name }}            {% endfor %}                        {% for name in name_list %}                {{ name }}            {% empty %}                空空如也          

使用DRF视图集时自定义action方法

在我们用DRF视图集完成了查找全部部门,创建一个新的部门,查找一个部门,修改一个部门,删除一个部门的功能后,views.py的代码是这样子的: class DepartmentViewSet(ModelViewSet): queryset = Department.objects.all() serializer_class = DepartmentSerializer 现在需要新增一个功能,查询最新成立的部门. 这时,我们就需要自定义action方法了. 新增功能后的代码如下: class D

DRF路由组件

REST framework提供了两个router SimpleRouter DefaultRouter DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据. example1: from .views import BookViewSet from rest_framework.routers import DefaultRouter # 实例化DeaultRouter对象 route

drf之视图类与路由

视图 Django REST framwork 提供的视图的主要作用: 控制序列化器的执行(检验.保存.转换数据) 控制数据库查询的执行 2个视图基类 APIView rest_framework.views.APIView APIView是REST framework提供的所有视图的基类,继承自Django的View父类. APIView与View的不同之处在于: 传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象: 视图方法

【DRF框架】路由组件

视图组件涉及的路由补充: from rest_framework.viewsets import ViewSetMixin 对路由进行了重新的分发,重写了as_view() 方法,重新封装了请求方法 from rest_framework.viewsets import ViewSetMixin class ViewSetMixin(object): """ This is the magic. Overrides `.as_view()` so that it takes

drf框架 8 系统权限类使用 用户中心信息自查 token刷新机制 认证组件项目使用:多方式登录 权限组件项目使用:vip用户权限 频率组件 异常组件项目使用

系统权限类使用 图书接口:游客只读,用户可增删改查权限使用 from rest_framework.permissions import IsAuthenticatedOrReadOnly class BookViewSet(ModelViewSet): # 游客只读,用户可增删改查 permission_classes = [IsAuthenticatedOrReadOnly] queryset = models.Book.objects.all() serializer_class = se

DRF框架之 serializers 序列化组件

1. 什么是序列化,其实在python中我们就学了序列化工具json工具,就是吧信息存为类字典形式 2. DRF框架自带序列化的工具: serializers 3. DRF框架 serializers 分为:第一种 Serializer   第二种 ModelSerializer 第一种用法之 Serializer from django.db import models # Create your models here. class Book(models.Model): nid = mod

drf框架中分页组件

0922自我总结 drf框架中分页组件 普通分页(最常用) 自定制分页类 pagination.py from rest_framework.pagination import PageNumberPagination # 普通分页 class CarPageNumberPagination(PageNumberPagination): # 默认一页的条数 page_size = 3 # 用户可以自定义选择一页的条数,但最多显示5条 page_size_query_param = 'page_s