11 Book系列多表群操作 - 图1


Book系列连表接口

views.py

  1. from django.shortcuts import render
  2. # Create your views here.
  3. from rest_framework.views import APIView
  4. from rest_framework.viewsets import ModelViewSet
  5. from app01.models import Book
  6. # from app01.ser import BookSerializers
  7. from rest_framework.decorators import action
  8. from rest_framework.response import Response
  9. from rest_framework.authentication import SessionAuthentication, BasicAuthentication
  10. # class TestView(APIView):
  11. # def get(self,request):
  12. # 1/0
  13. # return Response({'msg':'个人中心'})
  14. #
  15. # class BookViewSet(ModelViewSet):
  16. # authentication_classes = [BasicAuthentication,]
  17. # queryset = Book.objects.all()
  18. # serializer_class = BookSerializers
  19. # @action(methods=['get'], detail=False)
  20. # def login(self, request):
  21. # Book.objects.update_or_create()
  22. # return Response({'msg':'登陆成功'})
  23. # @action(methods=['put'], detail=True)
  24. # def get_new_5(self, request,pk):
  25. # return Response({'msg':'获取5条数据成功'})
  26. from rest_framework.permissions import AllowAny,IsAuthenticated,IsAdminUser,IsAuthenticatedOrReadOnly
  27. from app01.response import APIResponse
  28. from app01 import models
  29. from app01 import ser as serializers
  30. class PublishAPIView(APIView):
  31. def get(self, request, *args, **kwargs):
  32. pk = kwargs.get('pk')
  33. if pk:
  34. publish_obj = models.Publish.objects.filter(pk=pk).first()
  35. if not publish_obj:
  36. return APIResponse(1, 'pk error', http_status=400)
  37. publish_data = serializers.PublishModelSerializer(publish_obj).data
  38. return APIResponse(results=publish_data)
  39. publish_query = models.Publish.objects.all()
  40. return APIResponse(0, 'ok', data=serializers.PublishModelSerializer(publish_query, many=True).data)
  41. class BookAPIView(APIView):
  42. # 单查、群查
  43. def get(self, request, *args, **kwargs):
  44. pk = kwargs.get('pk')
  45. if pk:
  46. book_obj = models.Book.objects.filter(pk=pk, is_delete=False).first()
  47. if not book_obj:
  48. return APIResponse(1, 'pk error', http_status=400)
  49. book_data = serializers.BookModelSerializer(book_obj).data
  50. print(book_data)
  51. return APIResponse(data=book_data)
  52. book_query = models.Book.objects.filter(is_delete=False).all()
  53. return APIResponse(0, 'ok', data=serializers.BookModelSerializer(book_query, many=True).data)
  54. # 单删、群删
  55. def delete(self, request, *args, **kwargs):
  56. """
  57. 单删:前台数据为pk,接口为 /books/(pk)/
  58. 群删:前台数据为pks,接口为 /books/
  59. """
  60. pk = kwargs.get('pk')
  61. # 将单删群删逻辑整合
  62. if pk: # /books/(pk)/的接口就不考虑群删,就固定为单删
  63. pks = [pk]
  64. else:
  65. pks = request.data.get('pks')
  66. # 前台数据有误(主要是群删没有提供pks)
  67. if not pks:
  68. return APIResponse(1, 'delete error', http_status=400)
  69. # 只要有操作受影响行,就是删除成功,反之失败
  70. rows = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
  71. if rows:
  72. return APIResponse(0, 'delete ok')
  73. return APIResponse(1, 'delete failed')
  74. # 单增、群增
  75. def post(self, request, *args, **kwargs):
  76. """
  77. 单增:前台提交字典,接口 /books/
  78. 群增:前台提交列表套字典,接口 /books/
  79. """
  80. request_data = request.data
  81. if isinstance(request_data, dict): # 单增
  82. book_ser = serializers.BookModelSerializer(data=request_data)
  83. if book_ser.is_valid():
  84. book_obj = book_ser.save()
  85. return APIResponse(data=serializers.BookModelSerializer(book_obj).data)
  86. return APIResponse(1, msg=book_ser.errors)
  87. elif isinstance(request_data, list) and len(request_data) != 0 : # 群增
  88. book_ser = serializers.BookModelSerializer(data=request_data, many=True)
  89. book_ser.is_valid(raise_exception=True)
  90. book_obj_list = book_ser.save()
  91. return APIResponse(data=serializers.BookModelSerializer(book_obj_list, many=True).data)
  92. else:
  93. return APIResponse(1, 'data error', http_status=400)
  94. # 单整体改、群整体改
  95. def put(self, request, *args, **kwargs):
  96. """
  97. 单整体改:前台提交字典,接口 /books/(pk)/
  98. 群整体改:前台提交列表套字典,接口 /books/,注每一个字典都可以通过pk
  99. """
  100. pk = kwargs.get('pk')
  101. request_data = request.data
  102. if pk: # 单改
  103. try:
  104. book_obj = models.Book.objects.get(pk=pk)
  105. except:
  106. return APIResponse(1, 'pk error')
  107. # 修改和新增,都需要通过数据,数据依旧给data,修改与新增不同点,instance要被赋值为被修改对象
  108. book_ser = serializers.BookModelSerializer(instance=book_obj, data=request_data)
  109. book_ser.is_valid(raise_exception=True)
  110. book_obj = book_ser.save()
  111. return APIResponse(data=serializers.BookModelSerializer(book_obj).data)
  112. else: # 群改
  113. if not isinstance(request_data, list) or len(request_data) == 0:
  114. return APIResponse(1, 'data error', http_status=400)
  115. # [{pk:1,...}, {pk:3,...}, {pk:100,...}] => [obj1, obj3, obj100] + [{...}, {...}, {...}]
  116. # 要考虑pk对应的对象是否被删,以及pk没有对应的对象
  117. # 假设pk3被删,pk100没有 => [obj1] + [{...}]
  118. # 注:一定不要在循环体中对循环对象进行增删(影响对象长度)的操作
  119. obj_list = []
  120. data_list = []
  121. for dic in request_data:
  122. # request_data可能是list,单内部不一定是dict
  123. try:
  124. pk = dic.pop('pk')
  125. try:
  126. obj = models.Book.objects.get(pk=pk, is_delete=False)
  127. obj_list.append(obj)
  128. data_list.append(dic)
  129. except:
  130. pass
  131. except:
  132. return APIResponse(1, 'data error', http_status=400)
  133. book_ser = serializers.BookModelSerializer(instance=obj_list, data=data_list, many=True)
  134. book_ser.is_valid(raise_exception=True)
  135. book_obj_list = book_ser.save()
  136. return APIResponse(data=serializers.BookModelSerializer(book_obj_list, many=True).data)
  137. # 单局部改、群局部改
  138. def patch(self, request, *args, **kwargs):
  139. """
  140. 单整体改:前台提交字典,接口 /books/(pk)/
  141. 群整体改:前台提交列表套字典,接口 /books/,注每一个字典都可以通过pk
  142. """
  143. pk = kwargs.get('pk')
  144. request_data = request.data
  145. if pk:
  146. try:
  147. book_obj = models.Book.objects.get(pk=pk)
  148. except:
  149. return APIResponse(1, 'pk error')
  150. # 局部修改就是在整体修改基础上设置partial=True,将所有参与反序列化字段设置为required=False
  151. book_ser = serializers.BookModelSerializer(instance=book_obj, data=request_data, partial=True)
  152. book_ser.is_valid(raise_exception=True)
  153. book_obj = book_ser.save()
  154. return APIResponse(data=serializers.BookModelSerializer(book_obj).data)
  155. else: # 群改
  156. if not isinstance(request_data, list) or len(request_data) == 0:
  157. return APIResponse(1, 'data error', http_status=400)
  158. # [{pk:1,...}, {pk:3,...}, {pk:100,...}] => [obj1, obj3, obj100] + [{...}, {...}, {...}]
  159. # 要考虑pk对应的对象是否被删,以及pk没有对应的对象
  160. # 假设pk3被删,pk100没有 => [obj1] + [{...}]
  161. # 注:一定不要在循环体中对循环对象进行增删(影响对象长度)的操作
  162. obj_list = []
  163. data_list = []
  164. for dic in request_data:
  165. # request_data可能是list,单内部不一定是dict
  166. try:
  167. pk = dic.pop('pk')
  168. try:
  169. obj = models.Book.objects.get(pk=pk, is_delete=False)
  170. obj_list.append(obj)
  171. data_list.append(dic)
  172. except:
  173. pass
  174. except:
  175. return APIResponse(1, 'data error', http_status=400)
  176. book_ser = serializers.BookModelSerializer(instance=obj_list, data=data_list, many=True, partial=True)
  177. book_ser.is_valid(raise_exception=True)
  178. book_obj_list = book_ser.save()
  179. return APIResponse(data=serializers.BookModelSerializer(book_obj_list, many=True).data)
  180. class AuthorAPIView(APIView):
  181. def get(self,request,*args,**kwargs):
  182. authors=models.Author.objects.all()
  183. author_ser=serializers.AuthorModelSerializer(authors,many=True)
  184. return APIResponse(data=author_ser.data)
  185. def put(self,reuqest,*args,**kwargs):
  186. pass
  187. def post(self,request,*args,**kwargs):
  188. author_ser=serializers.AuthorModelSerializer(data=request.data)
  189. author_ser.is_valid(raise_exception=True)
  190. author_ser.save()
  191. return APIResponse()
  192. def delete(self,request,*args,**kwargs):
  193. pass

ser.py

  1. from rest_framework import serializers
  2. from app01 import models
  3. class BookListSerializer(serializers.ListSerializer):
  4. # 1、create方法父级ListSerializer已经提供了
  5. # def create(self, validated_data):
  6. # # 通过self.child来访问绑定的ModelSerializer
  7. # print(self.child)
  8. # raise Exception('我不提供')
  9. # 2、父级ListSerializer没有通过update方法的实现体,需要自己重写
  10. def update(self, instance, validated_data):
  11. # print(instance)
  12. # print(validated_data)
  13. return [
  14. self.child.update(instance[i], attrs) for i, attrs in enumerate(validated_data)
  15. ]
  16. class BookModelSerializer(serializers.ModelSerializer):
  17. # 通过BookModelSerializer.Meta.list_serializer_class来访问绑定的ListSerializer
  18. class Meta:
  19. # 关联ListSerializer完成群增群改
  20. list_serializer_class = BookListSerializer
  21. model = models.Book
  22. # fields = ('name', 'price', 'publish', 'authors')
  23. # fields = ('name', 'price', 'publish_name', 'author_list')
  24. # 了解
  25. # fields = '__all__'
  26. # exclude = ('id', )
  27. # depth = 1
  28. # 序列化与反序列化整合
  29. fields = ('name', 'price', 'publish_name', 'author_list', 'publish', 'authors')
  30. extra_kwargs = {
  31. 'publish': {
  32. 'write_only': True
  33. },
  34. 'authors': {
  35. 'write_only': True
  36. }
  37. }
  38. # 前提:如果只有查需求的接口,自定义深度还可以用子序列化方式完成
  39. class PublishModelSerializer(serializers.ModelSerializer):
  40. # 子序列化都是提供给外键(正向方向)完成深度查询的,外键数据是唯一:many=False;不唯一:many=True
  41. # 注:只能参与序列化,且反序列化不能写(反序列化外键字段会抛异常)
  42. books = BookModelSerializer(many=True)
  43. class Meta:
  44. model = models.Publish
  45. fields = ('name', 'address', 'books')
  46. class AuthorModelSerializer(serializers.ModelSerializer):
  47. class Meta:
  48. model=models.Author
  49. fields=('name','sex','mobile','mobile_in')
  50. extra_kwargs={
  51. 'mobile':{
  52. 'read_only': True
  53. },
  54. }
  55. mobile_in=serializers.CharField(write_only=True)
  56. # def validate_mobile_in(self, data):
  57. # print(data)
  58. # return data
  59. def create(self, validated_data):
  60. print(validated_data)
  61. mobile=validated_data.pop('mobile_in')
  62. author=models.Author.objects.create(**validated_data)
  63. authordetail=models.AuthorDetail.objects.create(mobile=mobile,author=author)
  64. return author

models.py

  1. from django.db import models
  2. # 一、基表
  3. # Model类的内部配置Meta类要设置abstract=True,这样的Model类就是用来作为基表
  4. # 多表:Book,Publish,Author,AuthorDetail
  5. class BaseModel(models.Model):
  6. is_delete = models.BooleanField(default=False)
  7. create_time = models.DateTimeField(auto_now_add=True)
  8. class Meta:
  9. # 基表必须设置abstract,基表就是给普通Model类继承使用的,设置了abstract就不会完成数据库迁移完成建表
  10. abstract = True
  11. class Book(BaseModel):
  12. name = models.CharField(max_length=16)
  13. price = models.DecimalField(max_digits=5, decimal_places=2)
  14. publish = models.ForeignKey(to='Publish', related_name='books', db_constraint=False, on_delete=models.DO_NOTHING)
  15. # 重点:多对多外键实际在关系表中,ORM默认关系表中两个外键都是级联
  16. # ManyToManyField字段不提供设置on_delete,如果想设置关系表级联,只能手动定义关系表
  17. authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)
  18. # 自定义连表深度,不需要反序列化,因为自定义插拔属性不参与反序列化
  19. @property
  20. def publish_name(self):
  21. return self.publish.name
  22. @property
  23. def author_list(self):
  24. temp_author_list = []
  25. for author in self.authors.all():
  26. temp_author_list.append({
  27. 'name': author.name,
  28. 'sex': author.get_sex_display(),
  29. 'mobile': author.detail.mobile
  30. })
  31. return temp_author_list
  32. class Publish(BaseModel):
  33. name = models.CharField(max_length=16)
  34. address = models.CharField(max_length=64)
  35. class Author(BaseModel):
  36. name = models.CharField(max_length=16)
  37. sex = models.IntegerField(choices=[(0, '男'),(1, '女')], default=0)
  38. class AuthorDetail(BaseModel):
  39. mobile = models.CharField(max_length=11)
  40. # 有作者可以没有详情,删除作者,详情一定会被级联删除
  41. # 外键字段为正向查询字段,related_name是反向查询字段
  42. author = models.OneToOneField(to='Author', related_name='detail', db_constraint=False, on_delete=models.CASCADE)
  43. # 二、表断关联
  44. # 1、表之间没有外键关联,但是有外键逻辑关联(有充当外键的字段)
  45. # 2、断关联后不会影响数据库查询效率,但是会极大提高数据库增删改效率(不影响增删改查操作)
  46. # 3、断关联一定要通过逻辑保证表之间数据的安全
  47. # 4、断关联
  48. # 5、级联关系
  49. # 作者没了,详情也没:on_delete=models.CASCADE
  50. # 出版社没了,书还是那个出版社出版:on_delete=models.DO_NOTHING
  51. # 部门没了,员工没有部门(空不能):null=True, on_delete=models.SET_NULL
  52. # 部门没了,员工进入默认部门(默认值):default=0, on_delete=models.SET_DEFAULT
  53. # 三、ORM外键设计
  54. # 1、一对多:外键放在多的一方
  55. # 2、多对多:外键放在常用的一方
  56. # 3、一对一:外键放在不常用的一方
  57. # 4、外键字段为正向查询字段,related_name是反向查询字段
  58. # from django.contrib.auth.models import AbstractUser, User
  59. # class MyUser(AbstractUser):
  60. # pass

setting.py

  1. LANGUAGE_CODE = 'zh-hans'
  2. TIME_ZONE = 'Asia/shanghai'
  3. USE_I18N = True
  4. USE_L10N = True
  5. USE_TZ = False

urls.py

  1. path(r'publishes/', views.PublishAPIView.as_view()),
  2. re_path(r'^publishes/(?P<pk>\d+)/$', views.PublishAPIView.as_view()),
  3. path(r'books/', views.BookAPIView.as_view()),
  4. re_path(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view()),