关键词搜索

源码搜索 ×
×

分别基于 Django_rest_framework 的 APIView、GenericAPIView 写的增删查改接口

发布2021-08-20浏览360次

详情内容

为演示方便,增删查改接口仅限于单张book表,其他表方法一样,数据库使用的是自带的 sqlite3。

写接口前的准备

创建模型表

  1. # models.py
  2. from django.db import models
  3. class BaseModel(models.Model):
  4. """
  5. 将图书表、出版社表、作者表都有的三个字段写在一个 BaseModel 类中,直接继承即可
  6. 但是本 BaseModel 类也会生成一张表,如果不想要这张表,可以在内部的 Meta 类中定义 abstract=True
  7. """
  8. is_delete = models.BooleanField(default=False) # 默认是 False,不删除
  9. # 创建时间
  10. create_time = models.DateTimeField(auto_now_add=True)
  11. # auto_now_add=True 只要记录创建,不需要手动插入时间,自动将当前时间插入
  12. # 另一种写法:
  13. # import datetime
  14. # create_time = models.DateTimeField(default=datetime.datetime.now) # now 不能加括号,不然记录的是每次程序启动的时间
  15. # 最后更新时间
  16. last_update_time = models.DateTimeField(auto_now=True)
  17. # auto_now=True 只要更新,就把当前时间插入
  18. class Meta:
  19. # 单个字段,有索引,可唯一
  20. # 多个字段,有联合索引,可联合唯一
  21. abstract = True # 抽象表,不在数据库中建立出表
  22. class Book(BaseModel):
  23. id = models.AutoField(primary_key=True) # id 字段可以不写,不写默认也有。但所有的表最好统一,写就都写
  24. name = models.CharField(max_length=32, help_text='这里填书名')
  25. price = models.DecimalField(max_digits=5, decimal_places=2)
  26. """ 外键 """
  27. publish = models.ForeignKey(to='Publish', on_delete=models.DO_NOTHING, db_constraint=False)
  28. # to_field 参数默认不用写,作用是关联到 Publish 主键
  29. # db_constraint=False 表示这个外键只是逻辑上的关联,实质上没有外键联系。但不会影响增删改和 orm 查询
  30. """
  31. 表断关联:
  32. 1、表之间没有外键关联,但是有外键逻辑关联(有充当外键的字段)
  33. 2、断关联后不会影响数据库查询效率,但是会极大的提高数据库增删改的效率(不影响增删改查操作)
  34. 3、断关联一定要通过逻辑保证表之间数据的安全,不要出现脏数据(一般由程序员在代码层面限制)
  35. 4、断关联后的级联关系:
  36. 作者删掉,作者详情也跟着删掉:on_delete=models.CASCADE
  37. 出版社删掉,图书还在:on_delete=models.DO_NOTHING
  38. 部门删掉,员工还在,员工的部门可以为空:null=True,on_delete=models.SET_NULL
  39. 部门删掉,员工进入默认部门(默认值):default=0, on_delete=models.SET_DEFAULT
  40. """
  41. # 多对多的第三张表,只有关联字段时,用自动就够了,有扩展字段,用半自动
  42. # 因为第三张表一共有三张表互相关联,所以不能写 on_delete
  43. authors = models.ManyToManyField(to='Author', db_constraint=False)
  44. def __str__(self):
  45. return self.name
  46. class Meta:
  47. verbose_name_plural = '图书表' # admin 中显示中文表名
  48. @property
  49. def publish_name(self):
  50. return self.publish.name
  51. def author_list(self):
  52. author_list = self.authors.all()
  53. lis = []
  54. for author in author_list:
  55. lis.append({'name': author.name, 'sex': author.get_sex_display()})
  56. return lis
  57. # 也可以用列表推导式一行完成
  58. # return [{'name': author.name, 'sex': author.get_sex_display()} for author in author_list]
  59. class Publish(BaseModel):
  60. id = models.AutoField(primary_key=True)
  61. name = models.CharField(max_length=32)
  62. addr = models.CharField(max_length=32)
  63. def __str__(self):
  64. return self.name
  65. class Meta:
  66. verbose_name_plural = '出版社表'
  67. class Author(BaseModel):
  68. name = models.CharField(max_length=32)
  69. sex = models.IntegerField(choices=((1, '男'), (2, '女')))
  70. """ 外键 """
  71. # OneToOneField 本质就是 ForeignKey + unique
  72. authordetail = models.OneToOneField(to='AuthorDetail', db_constraint=False, on_delete=models.CASCADE)
  73. def __str__(self):
  74. return self.name
  75. class Meta:
  76. verbose_name_plural = '作者表'
  77. class AuthorDetail(BaseModel):
  78. mobile = models.CharField(max_length=11)
  79. def __str__(self):
  80. return self.name
  81. class Meta:
  82. verbose_name_plural = '作者详情表'

写一个序列化类

  1. # ser.py
  2. from rest_framework import serializers
  3. from api import models
  4. # 写一个类,继承 ListSerializer,重写 update
  5. class BookListSerializer(serializers.ListSerializer):
  6. # 重写 create 方法,加了一个打印 validated_data 功能
  7. # def create(self, validated_data):
  8. # print(validated_data)
  9. # return super().create(validated_data)
  10. def update(self, instance, validated_data):
  11. # instance ==> [book对象1, book对象2 ...]
  12. # validated_data ==> [{name: xx, price: xx}, {name: xx, price: xx} ...]
  13. # self.child 是 BookModelSerializer 对象,
  14. # 想保存数据,可以使用 BookModelSerializer 的 update 方法,
  15. return [
  16. # self.child.update(对象,字典) for attrs in validated_data
  17. self.child.update(instance[i], attrs) for i, attrs in enumerate(validated_data)
  18. # 通过 enumerate 枚举出 validated_data 这个列表的 index(i) 和 字典(attrs)
  19. ]
  20. class BookModelSerializer(serializers.ModelSerializer):
  21. # 如何让前端收到的数据显示的是名称而不是id号:
  22. # 方法一:
  23. # publish = serializers.CharField(source='publish.name') # 只序列化可以,反序列化就有问题了
  24. # 方法二,在 models.py 中对应的表里写一个方法 publish_name 就可以直接放在 fields 中
  25. """
  26. @property
  27. def publish_name(self):
  28. return self.publish.name
  29. """
  30. class Meta:
  31. list_serializer_class = BookListSerializer
  32. model = models.Book
  33. # fields = '__all__'
  34. # depth = 1 # 展示数据的深度,数字表示展示几层,很少用
  35. fields = ('name', 'price', 'authors', 'publish', 'publish_name', 'author_list')
  36. # 但前端会收到两个 publish,一个是 id 号,一个是名字。解决方法是定义一个为只写,一个为只读
  37. extra_kwargs = {
  38. 'authors': {'write_only': True},
  39. 'author_list': {'read_only': True},
  40. 'publish': {'write_only': True},
  41. 'publish_name': {'read_only': True},
  42. }

自定义异常处理和自定义Response

最好在项目文件夹下新建一个 utils 文件夹。
将自定义的异常处理写在 exceptions.py 中,放在 utils 文件夹内。
将自定义的Response写在 response.py 中,放在 utils 文件夹内。
但本次在视图类中没有使用自定义Response,只使用了自定义异常处理。

自定义异常处理

  1. # exceptions.py
  2. from rest_framework.views import exception_handler
  3. from rest_framework import status
  4. from rest_framework.response import Response
  5. def my_exception_handler(exc, context):
  6. response = exception_handler(exc, context)
  7. if not response: # 如果返回的是 None
  8. # if isinstance(exc, ZeroDivisionError): # 如果视图类中有特定错误就可以做出相应的返回
  9. # return Response(data={'status': 777, 'msg': '除以0的错误' + str(exc)}, status=status.HTTP_400_BAD_REQUEST)
  10. return Response(data={'status': 999, 'msg': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
  11. else: # 如果返回的是 response 对象
  12. return Response(data={'status': 888, 'msg': response.data.get('detail')}, status=status.HTTP_400_BAD_REQUEST)

自定义Response

  1. # response.py
  2. from rest_framework.response import Response
  3. class APIResponse(Response):
  4. def __init__(self, code=100, msg='成功', data=None, status=None, headers=None, **kwargs):
  5. dic = {'code': code, 'msg': msg}
  6. if data:
  7. dic = {'code': code, 'msg': msg, 'data': data}
  8. dic.update(kwargs)
  9. super().__init__(data=dic, status=status, headers=headers)

全局配置自定义的异常处理

  1. # settings.py
  2. REST_FRAMEWORK = {'EXCEPTION_HANDLER': 'utils.exceptions.my_exception_handler'}

配置URL

URL的配置使用了路由分发

  1. # 项目下的 urls.py
  2. from django.conf.urls import url, include
  3. from django.contrib import admin
  4. urlpatterns = [
  5. url(r'^admin/', admin.site.urls),
  6. url(r'^api/', include('api.urls'))
  7. ]
  8. # 应用下的 urls.py
  9. from django.conf.urls import url
  10. from api import views
  11. urlpatterns = [
  12. url(r'^books/$', views.BookAPIView.as_view()),
  13. url(r'^books/(?P<pk>\d+)', views.BookAPIView.as_view()),
  14. url(r'^books2/$', views.Book2APIView.as_view()),
  15. url(r'^books2/(?P<pk>\d+)', views.Book2APIView.as_view()),
  16. ]

视图类接口的编写

基于 APIView 的视图类接口

  1. from api import models
  2. from rest_framework.views import APIView
  3. from rest_framework.response import Response
  4. from api.ser import BookModelSerializer
  5. class BookAPIView(APIView):
  6. def get(self, request, *args, **kwargs):
  7. """查询单条数据和查询所有数据合在一个方法中"""
  8. # 查询单条数据
  9. if kwargs.get('pk', None):
  10. book = models.Book.objects.filter(pk=kwargs.get('pk')).first()
  11. book_ser = BookModelSerializer(instance=book)
  12. return Response(book_ser.data)
  13. # 查询所有数据
  14. book_list = models.Book.objects.all().filter(is_delete=False) # 查询所有没有被删的书
  15. book_list_ser = BookModelSerializer(book_list, many=True)
  16. return Response(data=book_list_ser.data)
  17. def post(self, request, *args, **kwargs):
  18. """同时具备增单条和增多条的功能"""
  19. # 如果是字典,就是单条数据新增
  20. if isinstance(request.data, dict):
  21. book_ser = BookModelSerializer(data=request.data)
  22. book_ser.is_valid(raise_exception=True)
  23. book_ser.save()
  24. return Response(data=book_ser.data)
  25. # 如果是列表,就是多条数据新增,列表内部是多个字典(单条数据)
  26. elif isinstance(request.data, list):
  27. book_ser = BookModelSerializer(data=request.data, many=True) # 增多条
  28. # 有了 many=True 这个参数,book_ser 就是 ListSerializer 的对象了
  29. # print(type(book_ser)) # 输出:<class 'rest_framework.serializers.ListSerializer'>
  30. book_ser.is_valid(raise_exception=True)
  31. book_ser.save() # ListSerializer 的 create 方法
  32. """
  33. create 方法内部就是一个for循环,self.child 就是 BookModelSerializer 对象
  34. 也就是说,增多条的 ListSerializer 内部其实就是for循环了一个个增单条的 BookModelSerializer
  35. def create(self, validated_data):
  36. return [
  37. self.child.create(attrs) for attrs in validated_data
  38. ]
  39. """
  40. return Response(data=book_ser.data)
  41. def put(self, request, *args, **kwargs):
  42. """同时具备单条修改和多条修改功能"""
  43. # 单条数据修改
  44. if kwargs.get('pk', None):
  45. book = models.Book.objects.filter(pk=kwargs.get('pk')).first()
  46. book_ser = BookModelSerializer(instance=book, data=request.data, partial=True)
  47. # partial=True:允许局部修改,如只修改书名和价格
  48. # 如果不写这个字段想达到同样的效果,要在 ser.py 的 extra_kwargs 中给每个字段都加上 require=False
  49. book_ser.is_valid(raise_exception=True)
  50. book_ser.save()
  51. return Response(data=book_ser.data)
  52. # 多条数据修改
  53. else:
  54. """
  55. 前端传来的数据格式必须是:[{id: 1, name: xx, price: xx}, {id: 2, name: xx, price: xx}]
  56. 要先将数据处理成:对象列表[book对象1, book对象2] 和 要修改的数据列表[{name: xx, price: xx}, {name: xx, price: xx}]
  57. """
  58. book_list = []
  59. modify_data = []
  60. for item in request.data:
  61. # 前端传来的 request.data 可能不是列表套字典的格式,直接处理可能会报错,所以最好有全局的异常处理进行捕获
  62. pk = item.pop('id')
  63. book = models.Book.objects.get(pk=pk)
  64. book_list.append(book)
  65. modify_data.append(item)
  66. """
  67. 处理好后再修改数据,有两种方案:
  68. 第一种方案:
  69. 直接在视图里写,简单
  70. for循环 book_ser = BookModelSerializer(instance=对象列表, data=修改的数据列表) 一个一个修改。
  71. """
  72. # for i, is_data in enumerate(modify_data):
  73. # book_ser = BookModelSerializer(instance=book_list[i], data=is_data)
  74. # book_ser.is_valid(raise_exception=True)
  75. # book_ser.save()
  76. # return Response('成功')
  77. """
  78. 第二种方案:
  79. 在序列化类中写,不常用
  80. book_ser = BookModelSerializer(instance=对象列表, data=修改的数据列表, many=True)
  81. 但要重写 ListSerializer 的 update 方法,
  82. 因为此时 book_ser 是 ListSerializer 的对象,而源码中没写 update 方法,
  83. 需要自己写一个类继承 ListSerializer,里面重写 update 方法。
  84. 问题在于如何让 BookModelSerializer 和自己写的类建立联系?
  85. 查看源码可知,ListSerializer 的父类 BaseSerializer 的 __new__() 中调用了 many_init()
  86. many_init() 内部的如下代码通过反射,在meta中找不到 list_serializer_class 时才会走 ListSerializer
  87. list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
  88. 因此可以在 ser.py 中自己写的 BookModelSerializer 的 Meta 类中定一个变量叫 list_serializer_class,
  89. 把自己写的 BookListSerializer 这个类赋给这个变量,在 BookListSerializer 内重写 update 方法
  90. 这样只要修改数据时 BookModelSerializer 传了 many=True,就走的是 BookListSerializer 的 update 方法
  91. 同样,如果重写了 create 方法,新增数据时只要传了 many=True,就走的是 BookListSerializer 的 create 方法
  92. """
  93. book_ser = BookModelSerializer(instance=book_list, data=modify_data, many=True)
  94. book_ser.is_valid(raise_exception=True)
  95. book_ser.save()
  96. return Response(book_ser.data)
  97. def delete(self, request, *args, **kwargs):
  98. """单个删除和批量删除"""
  99. pk = kwargs.get('pk') # 如果是单条删除,pk=要删除数据的id值,多条删除,pk=None
  100. pks = []
  101. if pk: # 如果有值,说明是单条删除
  102. pks.append(pk) # 即使是单条数据也放在一个列表中,和多条删除用同一个方法处理
  103. else: # 如果没有值,是多条删除
  104. # 如果是多条删除,前端发来的数据格式必须是:{'pks': [要删除的多条数据的id值]},这是下面一行代码规定的
  105. # 获取到要删除的多条id值组成的列表
  106. pks = request.data.get('pks')
  107. # 最后,不论是单条还是多条删除,pks都是一个列表,里面是要删除数据的id值
  108. ret = models.Book.objects.filter(pk__in=pks, is_delete=False).update(is_delete=True)
  109. """
  110. pk__in:后面必须等于一个列表,是依次拿出列表里的每一项
  111. is_delete=False:确保之前没有被删除过的数据被过滤出来
  112. .update(is_delete=True):用 update 方法加上 is_delete=True 来标识要被删除的数据
  113. ret:返回值是受影响的行数(如果是0,表示要删的数据之前都已被标识为已删除了)
  114. """
  115. if ret:
  116. return Response(data={'msg': '删除成功!删除了%s条数据' % ret})
  117. return Response(data={'msg': '没有要删除的数据...'})

基于 GenericAPIView 的视图python教程类接口

  1. from api import models
  2. from rest_framework.generics import GenericAPIView
  3. from rest_framework.response import Response
  4. from api.ser import BookModelSerializer
  5. class Book2APIView(GenericAPIView):
  6. queryset = models.Book.objects.all()
  7. serializer_class = BookModelSerializer
  8. def get(self, request, *args, **kwargs):
  9. """单查和群查"""
  10. if kwargs.get('pk'):
  11. book = self.get_object()
  12. book_ser = self.get_serializer(book)
  13. return Response(book_ser.data)
  14. # 查询所有数据
  15. book_list = self.get_queryset().filter(is_delete=False) # 查询所有没有被删的书
  16. book_list_ser = self.get_serializer(book_list, many=True)
  17. return Response(data=book_list_ser.data)
  18. def post(self, request, *args, **kwargs):
  19. """单增和群增"""
  20. if isinstance(request.data, dict):
  21. book_ser = self.get_serializer(data=request.data)
  22. book_ser.is_valid(raise_exception=True)
  23. book_ser.save()
  24. return Response(data=book_ser.data)
  25. elif isinstance(request.data, list):
  26. book_ser = self.get_serializer(data=request.data, many=True)
  27. book_ser.is_valid(raise_exception=True)
  28. book_ser.save()
  29. return Response(data=book_ser.data)
  30. def put(self, request, *args, **kwargs):
  31. """单修和群修"""
  32. if kwargs.get('pk'):
  33. book = self.get_object()
  34. book_ser = self.get_serializer(instance=book, data=request.data, partial=True)
  35. book_ser.is_valid(raise_exception=True)
  36. book_ser.save()
  37. return Response(data=book_ser.data)
  38. else:
  39. book_list = []
  40. modify_data = []
  41. for item in request.data:
  42. pk = item.pop('pk')
  43. book = self.get_queryset().get(pk=pk)
  44. # book = self.get_queryset().filter(pk=pk).first() # 这样写也可以
  45. book_list.append(book)
  46. modify_data.append(item)
  47. book_ser = self.get_serializer(instance=book_list, data=modify_data, many=True, partial=True)
  48. book_ser.is_valid(raise_exception=True)
  49. book_ser.save()
  50. return Response(book_ser.data)
  51. def delete(self, request, *args, **kwargs):
  52. """单删和多删"""
  53. pk = kwargs.get('pk')
  54. pks = []
  55. if pk:
  56. pks.append(pk)
  57. else:
  58. pks = request.data.get('pks')
  59. ret = self.get_queryset().filter(pk__in=pks, is_delete=False).update(is_delete=True)
  60. if ret:
  61. return Response(data={'msg': '删除成功!删除了%s条数据' % ret})
  62. return Response(data={'msg': '没有可删除的数据...'})

相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载