視圖集與路由組件及三大認證原理

視圖集與路由組件及三大認證原理

一、基於GenericAPIView的十大接口

一、單查、羣查、單增、單總體改、單局部改均可以直接使用繼承GenericAPIView和mixins對應的包下面的功能數據庫

二、單刪不能直接使用,由於默認提供的功能是刪除數據庫數據,而不是咱們自定義的is_delete字段值修改,因此須要咱們來實現django

三、除了羣查之外的羣接口咱們都須要本身來實現api

注:給序列化類的context={'request': request},序列化類就能夠自動補全後臺圖片的連接,不須要咱們手動拼接了app

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 - 羣改及相關邏輯

二、繼承generics包下的工具視圖類的六大基礎接口

一、單查羣查不能共存ide

二、單刪不會使用自帶的通常都重寫函數

# 基於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下的六大基礎接口

三、視圖集

一、 ViewSetMixin類存在理由推導

一、工具視圖類,能夠完成應對六大基礎接口,惟一的缺點就是單查和羣查不能共存(只須要配置queryset、serializer_class、lookup_field)工具

二、不能共存的緣由:RetrieveAPIView和ListAPIView都是get方法,無論帶不帶pk的get請求,只能映射給一個get方法源碼分析

三、修改映射關係來改善此缺點:將羣查映射給list,單查映射給retrieve方法,甚至能夠自定義映射關係post

二、繼承視圖集的視圖類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源碼分析

三、視圖集使用

一、能夠直接繼承ModelViewSet,實現六大繼承接口(是否重寫destroy方法或其餘自定義方法根據需求決定)

二、能夠繼承ReadOnlyModelViewSet,僅實現只讀(單查、羣查)

三、繼承Viewset類與Model類關係不是很密切的接口:登陸post請求,查詢操做,短信驗證碼接口,藉助第三方平臺

四、繼承繼承GenericViewSet類,必需要配合mixins包完成任意的組合

五、繼承以上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)

二、繼承ReadOnlyModelViewSet,僅實現只讀(單查、羣查)

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

四、路由組件

必須配合視圖集使用

一、常規使用

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
路由組件配合視圖集

二、自定義路由組件(瞭解)

新建自定義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

三、上傳文件接口

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

五、三大認證規則

 

六、RBAC - Role-Based Access Control 權限六表

Django的Auth組件採用的認證規則就是RBAC(基於角色的訪問控制)

1)是否須要分表 答案:不須要 理由:先後臺用戶共存的項目,後臺用戶量都是不多;作人員管理的項目,基本上都是後臺用戶;先後臺用戶量都大的會分兩個項目處理

2)用戶權限六表是否須要斷關聯 答案:不須要 理由:前臺用戶佔主導的項目,幾乎需求只會和User一個表有關;後臺用戶佔主導的項目,用戶量不會太大

3)Django項目有沒有必須自定義RBAC六表 答案:不須要 理由:auth組件功能十分強大且健全(驗證密碼,建立用戶等各類功能);admin、xadmin、jwt、drf-jwt組件都是依賴auth組件的(自定義RBAC六表,插件都須要自定義,成本極高)

三基礎表:

權限六表:

一、後臺用戶對各表操做,是後臺項目完成的,咱們能夠直接藉助admin後臺項目(Django自帶的)

二、後期也可使用xadmin框架來作後臺用戶權限管理

三、前臺用戶的權限管理如何處理

  • 定義一堆數據接口的視圖類,不一樣的登陸用戶是否能訪問這些視圖類,能就表明有權限,不能就表明沒有權限

  • 前臺用戶權限用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建立權限管理

 

相關文章
相關標籤/搜索