drf十大接口
提示:模型表models.py在文末
单查&群查
简单情况下,查询一条数据和查询多条数据是有两条URL(带pk的和不带pk的),对应两个APIView视图类。
复杂一点,可以将两个APIView视图类合并在一起,两条URL对应一个统一的URL。在视图类中通过判断pk值是否存在来选择单查还是群查。
其实,如果这个视图仅仅做查询接口使用,那么可以继承drf提供的ReadOnlyModelViewSet
,这样就不需要再重复写查询接口的代码。并且经过简单配置就可以使用分页功能。
# 基于APIView的群查和单查
class BookAPIView(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk', None)
if not pk:
# 过滤出没有被删除的数据
book_list = models.Book.objects.filter(is_delete=False).all()
ser_obj = ser.BookModelSerializer(book_list, many=True)
return Response(ser_obj.data)
else:
try:
book_obj = models.Book.objects.filter(is_delete=False).get(pk=pk)
except Exception:
return Response(data={'code': 1001, 'detail': '查询的数据不存在'})
ser_obj = ser.BookModelSerializer(book_obj)
return Response(ser_obj.data)
# urls.py
path('books/', views.BookAPIView.as_view()),
re_path(r'books/(?P<pk>\d+)/', views.BookAPIView.as_view()),
# 基于ReadOnlyModelViewSet的十大接口
class BookReadOnlyModelViewSet(ReadOnlyModelViewSet):
queryset = models.Book.objects.all()
serializer_class = ser.BookModelSerializer
pagination_class = ApiPageNumberPagination # 分页器
# urls.py
path('books3/', views.BookReadOnlyModelViewSet.as_view(actions={'get': 'list',})),
re_path(r'books3/(?P<pk>\d+)/', views.BookReadOnlyModelViewSet.as_view(actions={'get': 'retrieve'})),
单增&群增
增加数据提交POST请求,可以增加一条记录,也可以增加多条记录,对应的URL都是一条。
简单点,继承APIView
视图类,手动实现post方法,在post方法内分情况判断是增加一条记录还是增加多条记录。
当新增一条记录时,request.data
的中的数据是一个字典,当增加多条记录时request.data
中的数据是一个列表。
# views.py
class BookAPIView(APIView):
def post(self, request, *args, **kwargs):
recv_data = request.data
if isinstance(recv_data, dict):
ser_obj = ser.BookModelSerializer(data=recv_data)
ser_obj.is_valid(raise_exception=True)
ser_obj.save()
return Response(ser_obj.data)
elif isinstance(recv_data, list):
ser_obj = ser.BookModelSerializer(data=recv_data, many=True)
ser_obj.is_valid(raise_exception=True)
ser_obj.save()
return Response(ser_obj.data)
else:
return Response(data={'code': 1002, 'detail': '提交的数据格式不正确'})
"""
# 新增一条记录时,request.data应该是一个字典对象,里面包含了新增记录需要的所有必填字段数据
-此时直接使用模型类序列化器生成一个对象,数据校验合格后,调用模型类序列化器对象的save方法;
-内部会调用模型对象的create方法,完成新增一条记录。【模型类帮我么实现了create方法和update方法】
# 新增多条记录时,request.data应该是一个列表,列表里面包含了多个字典,字典内是每一条记录需要的字段数据
-此时传入模型类序列化器实例化时在传入列表的同时,需要指定参数many=True
-这是实例化的对象不再是模型类对象,而是一个drf内置的ListSeralizer对象,该类帮我们实现了create批量增加
-调用ListSeralizer对象的save方法时,内部会循环调用每一个模型类对象的create方法,实现批量增加多条记录
"""
单改&群改
修改一条数据,一把在url中指定要修改数据的id值,在请求体中携带新数据。
批量修改数据时,没有在url中体现多个对象的id值,而是在请求体中携带每个要修改记录的id值即新数据。
和批量增加多条记录类似,批量修改多条记录时,前段传过来的数据格式也要有限制,请求体中传过来的数据必须提现要修改对象的id值。
然后使用模型类序列化器实例化对象时传入的参数instance是一个包含对个模型对象的列表,data是移除了id值的待修改的新数据。同时传入参数many=True,内部会实例化一个ListSerializer
对象;
调用该对象的save方法,会调用其update方法,但是这个方法是需要被重写的,不然会报错。
因此,此处需要自定义一个继承了ListSerializer
对类,并重写update方法。该方法内循环调用模型类序列化器对象的update方法,是想批量修改多条记录。
默认情况下many=True
将创建一个drf内置的ListSerializer
对象,但该类没有update方法,因此我们需要指定实例化我们自己继承ListSerializer
子类的对象。
指定的方法很简单,就是模型类序列化器的meta类中指定即可,list_serializer_class = BookListSerializer
另外,不论是单改还是批量修改,修改一条数据时都可以指定全部修改还是局部修改,使用参数partial=True
,内部本质是让每个字段都为required=False
# views.py
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk', None)
if pk:
try:
book_obj = models.Book.objects.filter(is_delete=False).get(pk=pk)
ser_obj = ser.BookModelSerializer(instance=book_obj, data=request.data, partial=True)
ser_obj.is_valid(raise_exception=True)
ser_obj.save()
return Response(ser_obj.data)
except Exception:
return Response(data={'code': 1004, 'msg':'出错了', 'detail': '修改的对象不存在'})
else:
# 群修改, request.data的数据格式,[book1, book2, ...]
# 图书id列表
book_id_list = [item.pop('id') for item in request.data]
# 图书对象列表
book_obj_list = models.Book.objects.filter(is_delete=False).filter(id__in=book_id_list)
# ser_obj是自定义的ListSerializer对象,该类中需要重写update方法
# partial=True表示局部修改,即部分修改,本质上是让每个字段required=False
ser_obj = ser.BookModelSerializer(instance=book_obj_list,
data=request.data, many=True, partial=True)
ser_obj.is_valid(raise_exception=True)
ser_obj.save()
return Response(ser_obj.data)
# ser.py
class BookListSerializer(serializers.ListSerializer):
# 自定义ListSerializer并重写update方法
def update(self, instance, validated_data):
"""
# instance是待修改的图书列表,validated_data是提交的修改数据列表
# 返回一个修改后的对象列表,child是BookModelSerializer序列化器对象
# 循环调用该序列化器对象的update方法,返回修改后的图书对象
"""
return [
self.child.update(instance[index], validated_data=item)
for index, item in enumerate(validated_data)
]
class BookModelSerializer(serializers.ModelSerializer):
publish_id = serializers.IntegerField(write_only=True)
authors_id = serializers.ListField(write_only=True, source='authors')
publish = serializers.SerializerMethodField(read_only=True)
authors = serializers.SerializerMethodField(read_only=True)
def get_publish(self, instance):
publish = instance.publish
return {'id': publish.pk, 'name': publish.name, 'email': publish.email}
def get_authors(self, instance):
authors = instance.authors.all()
return [
{'id': author.pk, 'name': author.name, 'sex': author.sex}
for author in authors
]
class Meta:
model = models.Book
# 群修改操作,指定自定义ListSerializer类,并重写该类的update方法
list_serializer_class = BookListSerializer
fields = (
'id', 'name', 'price',
'publish', 'authors',
'publish_id', 'authors_id',
)
# depth = 1
单删&群删
实际开发中,一般不会将数据删除掉,而是使用一个布尔字段标识该字段是否被删除。
删除操作其实用不到序列化器,但是需要注意数据的删除操作,不是真是的删数据而是修改是否被删除字段。
# views.py
# 基于APIView的十大接口
class BookAPIView(APIView):
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk', None)
pks = []
if pk:
pks.append(pk)
else:
pks = request.data.get('pks', None)
ret = models.Book.objects.filter(is_delete=False).filter(id__in=pks).update(is_delete=True)
msg = '删除成功' if ret else '删除的数据不存在'
return Response(data=msg)
# 单删是通过url朝后端发送待删除数据的id值,群删则捅咕请求体将群删的id列表传给后端。
# 此处为了统一单删和多删的处理方式,将单删的记录的pk值也放在了pks这个列表中
# 删除操作,使用了orm的双下划线模糊查询__in来统一批量修改删除字典的不布尔值。
基于ModelViewSet的十大接口
基于ModelViewSet
的十大接口需要需要重写一些方法,比如群增的post方法和群改的put方法。
在群增时,千万记得记得不要在url中通过actions
参数指定post请求的处理方式为create()
在群改时,put请求直接来到自己写的put方法中,坑【url中actions参数】
删除时,需要重写perform_destroy
方法,不是真是的删数据库数据而是修改布尔字段。
# views.py
class BookModelViewSet(ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = ser.BookModelSerializer
pagination_class = ApiPageNumberPagination # 分页器
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(is_delete=False).all()
def perform_destroy(self, instance):
instance.is_delete = True
instance.save()
def post(self, request, *args, **kwargs):
recv_data = request.data
if isinstance(recv_data, dict):
return super().create(request, *args, **kwargs)
elif isinstance(recv_data, list):
ser_obj = ser.BookModelSerializer(data=recv_data, many=True)
ser_obj.is_valid(raise_exception=True)
ser_obj.save()
return Response(ser_obj.data)
else:
return Response(data={'code': 1002, 'detail': '提交的数据格式不正确'})
def put(self, request, *args, **kwargs):
# 群修改是执行次操作
# 群修改, [book1, book2, ...]
# 图书id列表
book_id_list = [item.pop('id') for item in request.data]
# 图书对象列表
book_obj_list = self.get_queryset().filter(id__in=book_id_list)
# ser_obj是自定义的ListSerializer对象,该类中需要重写update方法
print(request.data)
ser_obj = self.get_serializer(instance=book_obj_list, data=request.data, many=True, partial=True)
ser_obj.is_valid(raise_exception=True)
ser_obj.save()
return Response(ser_obj.data)
def delete(self, request, *args, **kwargs):
pks = request.data.get('pks', None)
ret = self.get_queryset().filter(id__in=pks).update(is_delete=True)
msg = '删除成功' if ret else '删除的数据不存在'
return Response(data=msg)
# urls.py
path('books3/', views.BookModelViewSet.as_view(actions={'get': 'list',})),
re_path(r'books3/(?P<pk>\d+)/', views.BookModelViewSet.as_view(actions={'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
模型表
from django.db import models
class BaseModel(models.Model):
create_time = models.DateTimeField(auto_now_add=True)
update_time = models.DateTimeField(auto_now=True)
is_delete = models.BooleanField(default=False)
class Meta:
abstract = True # 基表,通过该参数指定告知orm不要创建该表
class Book(BaseModel):
# 一张表中只能有一个自增字段
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32, verbose_name='图书名')
price = models.DecimalField(max_digits=6, decimal_places=2)
publish = models.ForeignKey(to='Publish', on_delete=models.DO_NOTHING, db_constraint=False)
authors = models.ManyToManyField(to='Author', db_constraint=False)
class Meta:
verbose_name_plural = '图书表'
def __str__(self):
return self.name
class Publish(BaseModel):
name = models.CharField(max_length=32, verbose_name='出版社')
email = models.EmailField()
class Meta:
verbose_name_plural = '出版社'
def __str__(self):
return self.name
class Author(BaseModel):
name = models.CharField(max_length=32, verbose_name='出版社')
sex = models.IntegerField(choices=((1, '男'), (2, '女')))
author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE, db_constraint=False)
class Meta:
verbose_name_plural = '作者表'
def __str__(self):
return self.name
class AuthorDetail(BaseModel):
phone = models.CharField(max_length=11)
age = models.IntegerField()
class Meta:
verbose_name_plural = '作者详情表'