目录
一、外键连表深度查询
(一)子序列化
- 必须有子序列化类配合,不能参与反序列化
- 字段名必须是外键字段,正向反向均可
- 自定义序列化字段是不能参与反序列化,而子序列化必须是自定义序列化字段(外键名),所以就无法入库
- 外键关联数据是多条时,需要明确many=True
- 子序列的类必须写在上方,是一种单向操作
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__'
class PublishModelSerializer(serializers.ModelSerializer):
books = BookModelSerializer(many=True)
class Meta:
model = models.Publish
# fields = '__all__'
fields = ['name', 'address', 'books']
(二)depth配置
- 自动深度查询,值代表深度次数
- 被深度的外键采用
__all__
,因此会显示所有字段值,无法自定义配置
- exclude属性:除了该字段
__all__
:除了_开头的以外的所有字段
class PublishModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Publish
fields = ['name', 'address', 'books']
# 了解配置
# fields = '__all__' # 包含所有,不包括_开头的变量,
# exclude = ['name'] # 除了name字段都会被显示
depth = 2 # 自动深度,值代表深度次数,但是被深度的外键采用__all__,显示所以字段
(三)@property自定义序列字段
- 名字不能与外键名同名
- 返回单个数据时,可以通过序列化类完成序列化,也可以手动序列化
- 返回多个数据时,通过遍历添加完成序列化
- 注意:要考虑到关联的表中没有相应数据的情况(断开关联时),可以通过try进行错误捕捉
# models.py
class Book(BaseModel):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=10, decimal_places=2)
publish = models.ForeignKey(to='Publish', related_name='books', db_constraint=False, on_delete=models.DO_NOTHING, null=True)
authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)
# 1. 一对多:单个数据(两种方式序列化)
@property
def publish_info(self):
# from .serializers import PublishModelSerializer
# return PublishModelSerializer(self.publish).data
return {
'name':self.publish.name,
'address':self.publish.address
}
# 2. 多对多:多个数据
@property
def author_list(self):
author_list_temp=[] # 存放所有作者格式化成数据的列表
authors = self.authors.all() # 所有作者
for author in authors: # 遍历处理所有作者
author_dic = {
'name':author.name,
}
# 防止出现作者没有对应详细信息(电话)的情况,因此用try捕捉
try:
author_dic['mobile'] = author.detail.mobile
except:
author_dic['mobile'] = '无'
author_list_temp.append(author_dic) # 把处理后的数据添加到数据列表中
return author_list_temp
二、二次封装Response
- 对drf的响应模块进行二次封装,减少代码的重复,使代码更加精简
通过新建response.py文件,在文件内将返回Response进行二次封装成一个类(面对对象封装)
要注意results不为None和results不为空是两个概念
# response.py
from rest_framework.response import Response
class APIResponse(Response):
def __init__(self,status=0,msg='ok',results=None,http_status=None,headers=None,exception=False,content_type=None,**kwargs):
# 将status、msg、results、kwargs、格式化成data
data = {
'status':status,
'msg':msg,
}
# 2. results只要不为空就都是数据,比如False、0、''都是数据,因此判断条件不能写 if results
if results is not None:
data['results'] = results
data.update(**kwargs)
super().__init__(data=data,status=http_status,headers=headers,exception=exception,content_type=content_type)
三、十大接口
- 一个视图、两个url,十个接口
- 视图类BookAPIView
- 两个url:
url(r'^books/$', views.BookAPIView.as_view()),
- `
url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view()),
- 十大接口(五大请求):get、post、put、patch、delete
(一)单查群查(get)
- 二次封装Response的使用
from . import models,serializers
from response import APIResponse
class BookAPIView(APIView):
# 单查群查
def get(self,request,*args,**kwargs):
pk = kwargs.get('pk')
if pk:
# 单查
book_obj = models.Book.object.filter(is_delete=False,pk=pk).first()
book_ser = serializers.BookModeSerialier(book_obj)
else:
# 多查
book_query = models.Book.object.filter(is_delete=False).all()
book_ser = serializers.BookModelSerialzer(book_query,many=True)
return APIResponse(results=book_ser.data)
'''
return Reponse(data={
'status':0,
'msg':'pk'
'results':book_ser.data
})
'''
(二)单删群删(delete)
- 逻辑:修改is_delete字段值,修改成功代表删除成功
- 单删群删:只要拼接参数中有值则为单删,否则为群删
- 删除操作时,要考虑到用户不存在、群删部分成功等情况,一律视为客户端请求数据有误
- 群删时,可以通过神奇的下划线方法pk__in进行操作
def delete(self,request,*args,**kwargs):
pk = kwargs.get('pk')
if pk:
pks = pk
else:
pks = request.data
# 3. 数据如果有误,数据库执行会出错
try:
rows = models.Book.object .filter(is_delete=False,pk__in=pks).update(is_dalete=True)
except:
return APIRsponse(1,'数据有误')
if rows:
return APIResponse(0,'删除成功')
return APIResponse(1,'删除失败')
(三)单增群增(post)
- 单增和群增都是同一个url(/books/),通过判断数据包参数是否是字典还是列表来确定是单增还是群增
- serrialzier对象的is_valid方法中的raise_exception属性为True时,如果脚丫内部通过,会自动返回错误对象给前端
def post(self,request,*args,**kwargs):
# 1. 判断数据包参数是字典还是列表来确定是单增还是群增
if instance(request.data,dict):
many = False
elif instance(request,data,list):
many = True
else:
return Response(data={'detail': '数据有误'}, status=400)
book_ser = serializers.BookModelSerilazer(data=request.data,many=many)
book_ser.is_valid(raise_exception = True) # 2. 通过自动返回错误对象
book_obj_or_list = book_ser.save()
return APIResponse(results=serializer,BookModelSerializer(book_obj_or_list,many=many).data)
(四)整体单改群改(put)
- 单改
- 与单增群增的区别在于:序列化数据时,是否有表数据对象instance
- 后台会检测如果有instance,则调用updata方法
- 如果没有instance,则调用create方法
- ModelSerializer类中只定义了create方法,没有定义update方法,因此update方法要手动定义
- 通过捕捉异常来实现,如果表数据对象不存在,返回给前端错误对象
- 与单增群增的区别在于:序列化数据时,是否有表数据对象instance
- 群改
- 通过列表套字典的数据传递,当某个字典中没有pk值,或者对应的表数据对象已被删除,或者不存在时,可以认为整个数据有误,直接抛出即可
def put(self,request,*args,**kwargs):
pk = kwargs.get('pk')
if pk: # 单改
try: # 3. 通过捕捉异常来实现,如果表数据对象不存在,返回给前端错误对象
book_instance = models.Book.object.get(is_delete=False,pk=pk)
except:
return Response({'detail':'pk error'},status=400)
# 1. 通过传入的instance是否为空,后台判断调用update还是create方法
book_ser = serializer.BookModelSerializer(instance=book_instance,data=request.data)
book_ser.is_valid(raise_exception=True)
book_obj = book_ser.save()
return APIResponse(results=serializer.BookModelSerialzier(book_obj).data)
else: # 群改
try:
pks=[]
for dic in request.data:
pk = dic.pop('pk') # 4.1 没有pk就会被捕捉异常
pks.append(pk)
book_query = models.Book.object.filter(is_delete=False,pk__in=pks).all()
if len(pks) != len(book_query):
raise Exception('pk对应的数据不存在')
except Exception as e:
return Response({'detail':f'{e}'},status=400)
book_ser = serializer.BookModelSerializer(instance=book_query,data=request.data,many=True)
book_ser.is_valid(raise_exception=True)
book_list = book_ser.save()
return APIResponse(results=serializer.BookModelSerializer(book_list,many=True).data)
(五)局部单改群改
局部更改和整体更改的唯一区别就在于partial属性值
- partial属性:序列化类中的partial=True,会让参与反序列化的字段都会置为选填字段
- 提供了值就改变,没提供的字段就不修改
- context属性:将context的值(字典类型)封装到序列化对象中
def patch(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk: # 单改
try:
book_instance = models.Book.objects.get(is_delete=False, pk=pk)
except:
return Response({'detail': 'pk error'}, status=400)
# partial属性值为True,反序列化的字段都会为选填字段,因此达到局部更改的目的
book_ser = serializers.BookModelSerializer(instance=book_instance, data=request.data, partial=True)
book_ser.is_valid(raise_exception=True)
book_obj = book_ser.save()
return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
else: # 群改
request_data = request.data
try:
pks = []
for dic in request_data:
pk = dic.pop('pk')
pks.append(pk)
book_query = models.Book.objects.filter(is_delete=False, pk__in=pks).all()
if len(pks) != len(book_query):
raise Exception('pk对应的数据不存在')
except Exception as e:
return Response({'detail': '%s' % e}, status=400)
# 通过context将其传入、在校验方法中,可以通过self.context就能拿到传入的值,比如request对象
book_ser = serializers.BookModelSerializer(instance=book_query, data=request_data, many=True, partial=True,context={'request': request})
book_ser.is_valid(raise_exception=True)
book_list = book_ser.save()
return APIResponse(results=serializers.BookModelSerializer(book_list, many=True).data)