视图集与路由组件及三大认证原理

视图集与路由组件及三大认证原理

1、基于GenericAPIView的十大接口

1、单查、群查、单增、单整体改、单局部改都可以直接使用继承GenericAPIView和mixins对应的包下面的功能

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)
        # 群查
        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 put(self, request, *args, **kwargs):
        if 'pk' in kwargs:
            # 单整体改
            return self.update(request, *args, **kwargs)
        # 群整体改,需要配合serializers中的ListSerializer重写update方法
        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)
        # 群局部改,需要配合serializers中的ListSerializer重写update方法
        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)
    
    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)
views.py - 基于GenericAPIView十大接口

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
            }
        }
serializers.py - 群改及相关逻辑

2、继承generics包下的工具视图类的六大基础接口

1、单查群查不能共存

2、单删不会使用自带的一般都重写

# 基于generics下的六大基础接口
from rest_framework import generics
class BookV2APIView(generics.RetrieveAPIView,
                    generics.ListAPIView,
                    generics.CreateAPIView,
                    generics.UpdateAPIView,
                    generics.DestroyAPIView):
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer
    # 单查和群查不能共存,所以我们重写get方法
    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)
基于generics下的六大基础接口

3、视图集

1、 ViewSetMixin类存在理由推导

1、工具视图类,可以完成应对六大基础接口,唯一的缺点就是单查和群查不能共存(只需要配置queryset、serializer_class、lookup_field)

2、不能共存的原因:RetrieveAPIView和ListAPIView都是get方法,不管带不带pk的get请求,只能映射给一个get方法

3、修改映射关系来改善此缺点:将群查映射给list,单查映射给retrieve方法,甚至可以自定义映射关系

2、继承视图集的视图类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函数,解析出actions中是什么请求,再通过dispatch完成分发
        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)
ViewSetMixin源码分析

3、视图集使用

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)

2、继承ReadOnlyModelViewSet,仅实现只读(单查、群查)

from rest_framework.viewsets import ReadOnlyModelViewSet
class BookV4APIView(ReadOnlyModelViewSet):
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer

4、路由组件

必须配合视图集使用

1、常规使用

urls.py

from django.conf.urls import url, include
from . import views
# 路由组件,必须配合视图集使用
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
​
# 以后就写视图集的注册即可:BookV3APIView和BookV4APIView都是视图集,路由后不需要加/,router中带的有
router.register('v3/books', views.BookV3APIView, 'book')
router.register('v4/books', views.BookV4APIView, 'book')
​
urlpatterns = [
    url('', include(router.urls))
]
路由组件urls

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
    
from rest_framework.viewsets import ReadOnlyModelViewSet
class BookV4APIView(ReadOnlyModelViewSet):
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer
路由组件配合视图集

2、自定义路由组件(了解)

新建自定义router.py文件,自定义后在urls.py中使用我们自定义的router

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={}
        ),
    ]
自定义router

3、上传文件接口

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

5、三大认证规则

 

6、RBAC - Role-Based Access Control 权限六表

Django的Auth组件采用的认证规则就是RBAC(基于角色的访问控制)

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

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

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

三基础表:

权限六表:

1、后台用户对各表操作,是后台项目完成的,我们可以直接借助admin后台项目(Django自带的)

2、后期也可以使用xadmin框架来做后台用户权限管理

3、前台用户的权限管理如何处理

  • 定义一堆数据接口的视图类,不同的登录用户是否能访问这些视图类,能就代表有权限,不能就代表没有权限

  • 前台用户权限用drf框架的三大认证

  • 前台用户权限会基于 jwt 认证

自定义User表

models.py

from django.db import models
​
# RBAC - Role-Based Access Control
# Django的 Auth组件 采用的认证规则就是RBAC
from django.contrib.auth.models import AbstractUser
# 自定义 User 表
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
models.py - 自定义User表

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)
admin.site.register(models.Book)
admin.site.register(models.Car)
自定义User表,admin界面管理User类

使用admin创建权限管理

 

 

 

 

posted @ 2020-02-23 21:42  Mr沈  阅读(224)  评论(0编辑  收藏  举报