一、多表查询序列化类外键字段的覆盖
""" 1)在序列化类中自定义字段,名字与model类中属性名一致,就称之为覆盖操作 (覆盖的是属性的所有规则:extra_kwargs中指定的简易规则、model字段提供的默认规则、数据库唯一约束等哪些规则) 2)外键覆盖字段用PrimaryKeyRelatedField来实现,可以做到只读、只写、可读可写三种形式 只读:read_only=True 只写:queryset=关联表的queryset, write_only=True 可读可写:queryset=关联表的queryset 3)当外界关联的数据是多个时,需标识many=True条件 """
from rest_framework import serializers from rest_framework import exceptions from . import models class BookModelSerializer(serializers.ModelSerializer): # 如何覆盖外键字段 # publish = serializers.PrimaryKeyRelatedField(read_only=True) # 只读覆盖 # publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all()) # 课读可写 queryset后面跟的就是绑定的类 # publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all(), write_only=True) # 只写 publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all()) authors = serializers.PrimaryKeyRelatedField(queryset=models.Author.objects.all(), many=True) class Meta: model = models.Book # fields = ['name', 'price', 'image', 'publish', 'authors', 'publish_name', 'author_list'] fields = ['name', 'price', 'image', 'publish', 'authors'] extra_kwargs = { 'image': { 'read_only': True, }, 'publish': { # 系统原有的外键字段要留给反序列化过程使用,序列化外键内容用@property自定义 'write_only': True, }, 'authors': { 'write_only': True, }, }
二、序列化类十大接口
1、单查群查
models.py
from django.db import models # Create your models here. 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) # related_name就是可以方向访问,比如主键在作者详情,作者访问作者详情就可以直接.detail就相当于来到了详情表 # db_constraint是控制两张表之间是否有关联,默认是True,改为False就是断关联 author = models.OneToOneField(to=Author, related_name='detail', db_constraint=False, # on_delete=models.CASCADE # on_delete=models.DO_NOTHING # default=1 on_delete=models.SET_NULL, null=True )
主路由urls.py
from django.conf.urls import url, include from django.contrib import admin from django.views.static import serve from django.conf import settings urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^api/', include('api.urls')), url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}) ]
子路由urls.py
from . import views from django.conf.urls import url urlpatterns = [ url(r'^books/$', views.BOOKAPIView.as_view()), url(r'^books/(?P<pk>d+)/$', views.BOOKAPIView.as_view()), ]
二次封装response.py
from rest_framework.response import Response class APIResponse(Response): def __init__(self, status=0, msg='ok', http_status=None, headers=None, exception=False, **kwargs): # 将外界传入的数据状态码,状态信息以及其他所有额外储存在kwargs中的信息,都格式化成data信息 data = { 'status': status, # 数据状态码 'msg': msg } if kwargs: data.update(kwargs) super().__init__(data=data, status=http_status, headers=headers, exception=exception)
serializers.py
from rest_framework import serializers from rest_framework import exceptions from . import models # 主序列化类 class BookModelSerializer(serializers.ModelSerializer): # 如何覆盖外键字段 # publish = serializers.PrimaryKeyRelatedField(read_only=True) # 只读覆盖 # publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all()) # 课读可写 queryset后面跟的就是绑定的类 # publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all(), write_only=True) # 只写 publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all()) authors = serializers.PrimaryKeyRelatedField(queryset=models.Author.objects.all(), many=True) class Meta: model = models.Book # fields = ['name', 'price', 'image', 'publish', 'authors', 'publish_name', 'author_list'] fields = ['name', 'price', 'image', 'publish', 'authors'] extra_kwargs = { 'image': { 'read_only': True, }, 'publish': { # 系统原有的外键字段要留给反序列化过程使用,序列化外键内容用@property自定义 'write_only': True, }, 'authors': { 'write_only': True, }, }
views.py
from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from . import models, serializers from . response import APIResponse # Create your views here. # 六个必备接口:单查,群查,单增,单删,单整体改(了解),单局部改 # 四个额外接口:群增,群删,群整体改,群局部改 class BOOKAPIView(APIView): # 序列化 # 单查群查 """ 单查:接口:/books/(pk)/ 群查:接口:/books/ """ def get(self, request, *args, **kwargs): pk = kwargs.get('pk') if pk: obj = models.Book.objects.filter(is_delete=False, pk=pk).first() serializer = serializers.BookModelSerializer(instance=obj) return APIResponse(result=serializer.data) else: queryset = models.Book.objects.filter(is_delete=False).all() serializer = serializers.BookModelSerializer(instance=queryset, many=True) return APIResponse(results=serializer.data)
2、单增群增
views.py
from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from . import models, serializers from . response import APIResponse # Create your views here. # 六个必备接口:单查,群查,单增,单删,单整体改(了解),单局部改 # 四个额外接口:群增,群删,群整体改,群局部改 class BOOKAPIView(APIView): """ # 反序列化 # 单增群增 """ 单增:接口:/books/ 数据:dict 群增:接口:/books/ 数据:list """ def post(self, request, *args, **kwargs): # 如何区别单增群增:request.data是{}还是[] if not isinstance(request.data, list): # isinstance()是用来判断一个对象是否是某个类型 # 单增 serializer = serializers.BookModelSerializer(data=request.data) serializer.is_valid(raise_exception=True) # 如果校验失败会直接抛异常返回给前台 obj = serializer.save() # 为什么要将新增的对象重新序列化给前台,因为序列化与反序列化数据不对等 return APIResponse(result=serializers.BookModelSerializer(obj).data, http_status=201) else: # 群增 serializer = serializers.BookModelSerializer(data=request.data, many=True) serializer.is_valid(raise_exception=True) # 如果校验失败会直接抛异常返回给前台 objs = serializer.save() # 为什么要将新增的对象重新序列化给前台,因为序列化与反序列化数据不对等 return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data, http_status=201)
3、单删群删
views.py
from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from . import models, serializers from . response import APIResponse # Create your views here. # 六个必备接口:单查,群查,单增,单删,单整体改(了解),单局部改 # 四个额外接口:群增,群删,群整体改,群局部改 class BOOKAPIView(APIView): # 单删群删 """ 单删:接口:/books/(pk)/ 群删:接口:/books/ 数据[pk1,...,pkn] """ def delete(self, request, *args, **kwargs): pk = kwargs.get('pk') if pk: pks = [pk] # 将单删伪装成群删一条 else: pks = request.data # 群删的数据就是群删的主键们 try: # request.data可能提交的数据是乱七八糟的,所以orm操作可能会异常 rows = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True) except: return APIResponse(1, '数据有误') if rows: # 只要有受影响的行,就代表删除成功 return APIResponse(0, '删除成功') return APIResponse(2, '删除失败')
4、单整体改群整体改
views.py
from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from . import models, serializers from . response import APIResponse # Create your views here. # 六个必备接口:单查,群查,单增,单删,单整体改(了解),单局部改 # 四个额外接口:群增,群删,群整体改,群局部改 class BOOKAPIView(APIView): # 单整体改 # 群整体改 """ 单整体改:接口:/books/(pk)/ 数据:dict 群整体改:接口:/books/ 数据[{pk1:xxx},...,{pkn:xxx}] """ def put(self, request, *args, **kwargs): pk = kwargs.pop('pk') if pk: # 单 try: instance = models.Book.objects.get(is_delete=False, pk=pk) except: return APIResponse(1, 'pk error', http_status=400) # 序列化类同时赋值instance和data,代表用data重新更新instance => 修改 serializer = serializers.BookModelSerializer(instance=instance, data=request.data) serializer.is_valid(raise_exception=True) obj = serializer.save() return APIResponse(result=serializers.BookModelSerializer(obj).data) else: # 群 """分析request.data数据 1)从request.data中分离出pks列表 2)pks中存放的pk在数据库中没有对应的数据,或者对应的数据已经被删除了,我们要把这些不合理的PK剔除 3)pks最终转换得到的objs 列表长度 与request.data 列表长度不一致,就是数据有误 """ pks = [] try: for dic in request.data: # 不是列表套字典不可以 pks.append(dic.get('pk')) # 是字典没有pk也不可以 objs = models.Book.objects.filter(is_delete=False, pk__in=pks) # null in pks也会异常 assert len(objs) == len(request.data) # 两个列表长度必须一致 except: return APIResponse(1, '数据有误', http_status=400) serializer = serializers.BookModelSerializer(instance=objs, data=request.data, many=True) serializer.is_valid(raise_exception=True) objs = serializer.save() # 为什么要将新增的对象重新序列化给前台,因为序列化与反序列化数据不对等 return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data)
serializers.py
from rest_framework import serializers from . import models # 群增群改辅助类(了解) class BookListSerializer(serializers.ListSerializer): """ 1)create群增方法不需要重新 2)update群改方法需要重写,且需要和views中处理request.data的逻辑配套使用 3)self.child就代表该ListSerializer类绑定的ModelSerializer类 BookListSerializer的self.child就是BookModelSerializer """ # 重新update方法 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') # fields = ('name', 'price', 'image', 'publish', 'authors', 'abc') extra_kwargs = { 'image': { 'read_only': True, }, 'publish': { # 系统原有的外键字段,要留给反序列化过程使用,序列化外键内容,用@property自定义 'write_only': True, }, 'authors': { 'write_only': True, }, } # 需求:内外传参 # 1)在钩子函数中,获得请求请求对象request 视图类 => 序列化类 # 2)序列化钩子校验过程中,也会产生一些数据,这些数据能不能传递给外界使用:序列化类 => 视图类 # 序列化类的context属性,被视图类与序列化类共享 def validate(self, attrs): print(self.context) # 可以获得视图类在初始化序列化对象时传入的context # self.context.update({'a': 10}) # 序列化类内部更新context,传递给视图类 return attrs
5、单局部改群局部改
views.py
from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from . import models, serializers from . response import APIResponse # Create your views here. # 六个必备接口:单查,群查,单增,单删,单整体改(了解),单局部改 # 四个额外接口:群增,群删,群整体改,群局部改 class BOOKAPIView(APIView): # 单局部改 # 群局部改 """ 单局部改:接口:/books/(pk)/ 数据:dict 群局部改:接口:/books/ 数据[{pk1:xxx},...,{pkn:xxx}] """ def patch(self, request, *args, **kwargs): pk = kwargs.get('pk') if pk: # 单局部改与单整体改就多了个partial=True try: instance = models.Book.objects.get(is_delete=False, pk=pk) except: return APIResponse(1, 'pk error', http_status=400) # partial=True的作用就是将所有的required改为false serializer = serializers.BookModelSerializer(instance=instance, data=request.data, partial=True) serializer.is_valid(raise_exception=True) obj = serializer.save() return APIResponse(result=serializers.BookModelSerializer(obj).data) else: # 群 pks = [] try: for dic in request.data: # 不是列表套字典不可以 pks.append(dic.get('pk')) # 是字典没有pk也不可以 objs = models.Book.objects.filter(is_delete=False, pk__in=pks) # null in pks也会异常 assert len(objs) == len(request.data) # 两个列表长度必须一致 except: return APIResponse(1, '数据有误', http_status=400) serializer = serializers.BookModelSerializer(instance=objs, data=request.data, many=True, partial=True, context={'request': request} # 初始化时,对Context赋值,将试图类中数据传递给序列化类 ) serializer.is_valid(raise_exception=True) objs = serializer.save() # 为什么要将新增的对象重新序列化给前台,因为序列化与反序列化数据不对等 print(serializer.context) # 在完成序列化类校验后,可以重新拿到序列化类内部对context做的值更新 return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data)
三、视图家族
""" 视图基类: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下的工具类的组合) 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