目录
视图家族 & 路由组件
视图基类:APIView、GenericAPIView
视图工具类:mixins包下的五个类(六个方法)
工具视图类:generics包下的所有GenericAPIView的子类
视图集:viewsets包下的类GenericAPIView基类(基本不会单独使用,了解即可,但是是高级视图类的依赖基础) 1)GenericAPIView继承APIView,所有APIView子类写法在继承GenericAPIView时可以保持一致 2)GenericAPIView给我们提供了三个属性 queryset、serializer_class、lookup_field 3)GenericAPIView给我们提供了三个方法 get_queryset、get_serializer、get_obj
mixins包存放了视图工具类(不能单独使用,必须配合GenericAPIView使用)
``` CreateModelMixin:单增工具类 create方法 ListModelMixin:群查工具类 list方法 RetrieveModelMixin:单查工具类 retrieve方法 UpdateModelMixin:单整体局部改工具类 update方法 DestroyModelMixin:单删工具类 destory方法generics包下的所有GenericAPIView的子类(就是继承GenericAPIView和不同mixins下的工具类的组合) ```python """ 1)定义的视图类,继承generics包下已有的特点的GenericAPIView子类,可以在只初始化queryset和serializer_class两个类属性后,就获得特定的功能 2)定义的视图类,自己手动继承GenericAPIView基类,再任意组合mixins包下的一个或多个工具类,可以实现自定义的工具视图类,获得特定的功能或功能们 注: i)在这些模式下,不能实现单查群查共存(可以加逻辑区分,也可以用视图集知识) ii)DestroyModelMixin工具类提供的destory方法默认是从数据库中删除数据,所以一般删除数据的需求需要自定义逻辑 """
urls.py
from django.conf.urls import url from . import views urlpatterns = [ # ... url(r'^v1/books/$', views.BookV1APIView.as_view()), url(r'^v1/books/(?P<pk>\d+)/$', views.BookV1APIView.as_view()), url(r'^v2/books/$', views.BookV2APIView.as_view()), url(r'^v2/books/(?P<pk>\d+)/$', views.BookV2APIView.as_view()), url(r'^v3/books/$', views.BookV3APIView.as_view()), url(r'^v3/books/(?P<pk>\d+)/$', views.BookV3APIView.as_view()), ]
views.py
# ----------------------------- 过渡写法:了解 ----------------------------- from rest_framework.generics import GenericAPIView class BookV1APIView(GenericAPIView): # 将数据和序列化提示为类属性,所有的请求方法都可以复用 queryset = models.Book.objects.filter(is_delete=False).all() serializer_class = serializers.BookModelSerializer lookup_field = 'pk' # 可以省略,默认是pk,与url有名分组对应的 # 群查 def get(self, request, *args, **kwargs): # queryset = models.Book.objects.filter(is_delete=False).all() # => 方法+属性两行代码 queryset = self.get_queryset() # serializer = serializers.BookModelSerializer(instance=queryset, many=True) # => 方法+属性两行代码 serializer = self.get_serializer(instance=queryset, many=True) return APIResponse(results=serializer.data) # 单查 # def get(self, request, *args, **kwargs): # obj = self.get_object() # serializer = self.get_serializer(obj) # return APIResponse(results=serializer.data) # 单增 def post(self, request, *args, **kwargs): # serializer = serializers.BookModelSerializer(data=request.data) serializer = self.get_serializer(data=request.data) # 同样的步骤多了,好处就来了 serializer.is_valid(raise_exception=True) obj = serializer.save() return APIResponse(result=self.get_serializer(obj).data, http_status=201) # ----------------------------- 过渡写法:了解 ----------------------------- from rest_framework.generics import GenericAPIView from rest_framework import mixins class BookV2APIView(GenericAPIView, mixins.RetrieveModelMixin, mixins.CreateModelMixin): queryset = models.Book.objects.filter(is_delete=False).all() serializer_class = serializers.BookModelSerializer # 单查 def get(self, request, *args, **kwargs): # obj = self.get_object() # serializer = self.get_serializer(obj) # return APIResponse(results=serializer.data) # return self.retrieve(request, *args, **kwargs) response = self.retrieve(request, *args, **kwargs) return APIResponse(result=response.data) # 单增 def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) # ----------------------------- 开发写法:常用 ----------------------------- from rest_framework.generics import RetrieveAPIView class BookV3APIView(RetrieveAPIView): queryset = models.Book.objects.filter(is_delete=False).all() serializer_class = serializers.BookModelSerializer # 单查 pass
视图集与路由组件
准备工作
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)总结:
配置queryset、serializer_class、lookup_field
要自己定义get、post等方法,内部调用retrieve、create方法
基于 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)总结:
配置queryset、serializer_class、lookup_field
重写get处理单查群查共存即可
delete方法是否重写看需求
视图集
解析actions,修改 请求分发 - 响应函数 映射关系
""" ViewSetMixin类存在理由推到 1)工具视图类,可以完成应付六大基础接口,唯一缺点是单查与群查接口无法共存 (list, retrieve, create, update, destory) (只配置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) """核心
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)总结:
ViewSetMixin 类:重写 as_view 方法
作用:as_view({"get": "list"}) 来自定义请求与响应的映射关系
两个视图集基类:
ViewSet: 与 Model 关系不是特别紧密
GenericViewSet : 与 Model 关系特别紧
两个GenericViewSet的子类:
ModelViewSet:六大基础接口共存
ReadOnlyModelViewSet:只读接口
注:都只需要配置queryset、serializer_class、lookup_field;根据需求决定是否重写某些方法
自己用两个视图集基类与 mixins 包形成自定义组合
路由组件:必须配合视图集使用
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 .router import SimpleRouter router = SimpleRouter() router.register('car', views.CarAPIView, 'car') urlpatterns = [ url('', include(router.urls)) ]
上传图片接口
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总结:
上传图片:前台提交form-data,类型选择文件类型,后台用request.data和request.FILES都可以访问
权限
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.namesettings.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)# 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 认证
来源:https://www.cnblogs.com/kai-/p/12348963.html