Book系列群操作
表设计与关联models.py
from django.db import models
# 一、基表
# Model类的内部配置Meta类要设置abstract=True,这样的Model类就是用来作为基表
# 基表不会在数据库内创建出来
# 二、表断关联
# 1、表之间没有外键关联,但是有外键逻辑关联(有充当外键的字段)
# 2、断关联后不会影响数据库查询效率,但是会极大提高数据库增删改效率(不影响增删改查操作)
# 3、断关联一定要通过逻辑保证表之间数据的安全,不要出现脏数据,代码控制
# 4、断关联
# 5、级联关系
# 作者没了,详情也没:on_delete=models.CASCADE
# 出版社没了,书还是那个出版社出版:on_delete=models.DO_NOTHING
# 部门没了,员工没有部门(空不能):null=True, on_delete=models.SET_NULL
# 部门没了,员工进入默认部门(默认值):default=0, on_delete=models.SET_DEFAULT
# 三、ORM外键设计
# 1、一对多:外键放在多的一方
# 2、多对多:外键放在常用的一方
# 3、一对一:外键放在不常用的一方
# 4、外键字段为正向查询字段,related_name是反向查询字段
# 一张表有且只有一个自增字段,有且只有一个主键
class BaseModel(models.Model):
is_delete = models.BooleanField(default=False)
# auto_now_add=True 只要记录创建,不需要手动插入时间,自动把当前时间插入
create_time = models.DateTimeField(auto_new_add=True)
# auto_now=True,只要更新,就会把当前时间插入
last_update_time = models.DateTimeField(auto_new=True)
# import datetime
# create_time=models.DateTimeField(default=datetime.datetime.now)
class Meta:
# 单个字段,有索引,有唯一
# 多个字段,有联合索引,联合唯一
abstract=True # 抽象表
class Book(BaseModel):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
#to_field 默认不写,关联到Publish主键
#db_constraint=False 逻辑上的关联,实质上没有外键练习,增删不会受外键影响,但是orm查询不影响
# 出版社被删,图书不受影响
publish = models.ForeignKey(to='Publish', on_delete=models.DO_NOTHING, db_constraint=False)
# 多对多关系不能设置on_delete,如果想设置关系表级联,只能手动定义关系表(纯手动或半自动)
# 什么时候用自动,什么时候用手动?第三张表只有关联字段,用自动 第三张表有扩展字段,需要手动写
authors = models.ManyToManyField(to='Author', db_constraint=False)
@property # 将方法包成数据属性,不参与反序列化操作
def publish_name(self):
# 返回出版社名字
return self.publish.name
@property
def author_list(self):
# 使用列表生成式生成一个个的作者名字,返回列表
return [instance.name for instance in self.authors.all()]
class Author(BaseModel):
name = models.CharField(max_length=32)
gender = models.IntegerField(choices=((1, '男'), (2, '女')), default=1)
# 作者被删,详情一定被删
detail = models.OneToOneField(to='Detail', on_delete=models.CASCADE, db_constraint=False)
class Detail(BaseModel):
phton = models.CharField(max_length=11)
class Publish(BaseModel):
name = models.CharField(max_length=32)
address = models.CharField(max_length=32)
序列化器编写
# ser.py
from rest_framework import serializers
from api import models
# 通过查看源码可知,群增时父类ListSerializer已经帮我写了create方法,但是群改的update方法需要我们自己重写
# 我们也可以在继承父类ListSerializer的BookListSerializer中重写create方法实现群增,但是没必要
class BookListSerializer(serializers.ListSerializer):
def update(self, instance, validated_data):
# self.child就是BookModelSerializer
return [self.child.update(instance[i], item) for i, item in enumerate(validated_data)]
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
# 通过list_serializer_class关联BookListSerializer,从而访问绑定的ListSerializer,实现群增群改
list_serializer_class = BookListSerializer
model = models.Book
# 插拔式属性,进行序列化操作,数据属性(publish_name,author_list)不参与反序列化操作
fields = ('pk', 'name', 'price', 'author_list', 'publish_name', 'publish', 'authors')
extra_kwargs = {
'publish': {'write_only': True},# 提交数据时填写,不会展示给用户看
'publish_name': {'read_only': True}, # 只展示,不会提交
'authors': {'write_only': True},
'author_list': {'read_only': True}
}
使用GenericAPIView实现5个接口
# views.py
from rest_framework.generics import GenericAPIView
from rest_framework import status
from api import models
from api import ser
from utils.response import APIResponse # 封装的response对象
class BooksGenericAPIView(GenericAPIView):
queryset = models.Book.objects.all().filter(is_delete=False) # 筛选出所有没被删除的数据
serializer_class = ser.BookModelSerializer # 写入好的序列化器
# 单查、群查
def get(self, request, *args, **kwargs):
if kwargs:# 通过kwargs是否有值来判断是查询单个数据还是查询所有数据
instance = self.get_object()
serializer = self.get_serializer(instance=instance)
else:
book_list = self.get_queryset()
serializer = self.get_serializer(instance=book_list, many=True)
return APIResponse(message='查询成功', result=serializer.data) # 返回自定义格式的json数据
# 单增、群增
def post(self, request, *args, **kwargs):
if isinstance(request.data, list): # 规定群增,前端传过来的必须是个列表
# save()调用继承的父类LiseSerializer中的create方法
serializer = self.get_serializer(data=request.data, many=True)
else:
# save()调用BookModelSerializer的create方法
serializer = self.get_serializer(data=request.data)
# 使用全局异常处理进行异常捕获,以json格式返回异常
serializer.is_valid(raise_exception=True)
serializer.save()
return APIResponse(message='创建成功', result=serializer.data, status=status.HTTP_201_CREATED)
# 单改、群改
def put(self, request, *args, **kwargs):
if kwargs:
instance = self.get_object()
# save()调用BookModelSerializer的update方法
serializer = self.get_serializer(instance=instance, data=request.data)
# 局部修改
# 局部修改就是在整体修改基础上设置partial=True,或者将所有参与反序列化字段设置为required=False
# serializer = self.get_serializer(instance=instance, data=request.data, partial=True)
else:
book_list = []# 用来存储书对象
modify_list = []# 用来存储修改的数据
for data in request.data:
pk = data.pop('pk')
book_list.append(models.Book.objects.get(pk=pk))# 将获取到的书对象追加到列表
modify_list.append(data)# 将需要修改的数据追加到列表
# save()会调用我们自己重写的update方法(ListSerializer没有帮我们写群改的update)
# BookModelSerializer通过list_serializer_class来关联BookListSerializer,从而访问绑定的ListSerializer从而实现群改,其中的self.child就是指BookModelSerializer
serializer = self.get_serializer(instance=book_list, data=modify_list, many=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return APIResponse(message='修改成功', result=serializer.data)
# 单删、群删
def delete(self, request, *args, **kwargs):
# 不管是单删还是群删都存入列表pks中,只需要通过__in来筛选即可
pks = []
if kwargs:
pk = kwargs.get('pk')
pks.append(pk)
else:
pks = request.data.get('pks')
# 规定群删除传入格式{"pks":[]}
res = models.Book.objects.filter(pk__in=pks, is_delete=False).update(is_delete=True)
if not res:
return APIResponse(code=101, message='要删除的数据不存在', status=status.HTTP_404_NOT_FOUND)
return APIResponse(message='成功删除%s条数据' % res)
路由设置
# urls.py
# 总路由:
path('api/', include('api.urls'))
# 子路由:
path('books/', views.BooksGenericAPIView.as_view()),
re_path(r'books/(?P<pk>\d+)', views.BooksGenericAPIView.as_view()),