我们之前讲django类视图操作数据库时,给大家提到过序列化和反序列化,我们再来把代码扒下来看下

  1. class ProjectView(View):
  2. # 查询所有数据
  3. def get(self, request):
  4. projects = Projects.objects.all()
  5. p = []
  6. for i in projects:
  7. print(i.name)
  8. p.append({'name': i.name})
  9. return JsonResponse(p, safe=False)
  10. # 创建数据
  11. def post(self, request):
  12. python_data = json.loads(request.body)
  13. project_obj = Projects.objects.create(name=python_data['name'])
  14. python_dict = {
  15. 'name': project_obj.name,
  16. }
  17. return JsonResponse(python_dict, status=201)

序列化

我们查询所有数据的时候,把模型对象转换son字符串(完整步骤:1. 模型对象转化为python里面的基本类型 2. 把python基本数据类型转换为json字符串)

反序列化

我们创建数据的时候,其实经历了反序列化和序列化的2个过程。

  1. 将json格式数据转换为模型对象(反序列化)(完整步骤:1. json参数并转化为python中的数据类型2.把python中的数据类型转化为模型对象)
  2. 然后把模型对象转换son字符串(序列化输出)

其实这样转来转去的还是蛮复杂的,DRF 中有个serializers 序列化器完美帮我们解决了这个问题。

serializers 序列化器

首先我们在project 下新建serializers.py文件,并添加以下内容

  1. from rest_framework import serializers
  2. from .models import Projects
  3. class ProjectsSerializer(serializers.Serializer):
  4. name = serializers.CharField(max_length=200, min_length=2)
  5. desc = serializers.CharField(max_length=200, allow_blank=True)

序列化器类的第一部分定义了序列化/反序列化的字段(models.py)。
序列化器类与Django Form类非常相似,并在各种字段中包含类似的验证标志,例如required,max_length和default。
修改project\views.py文件

  1. import json
  2. from django.http import JsonResponse, Http404
  3. from django.views import View
  4. from .models import Projects
  5. from .serializers import ProjectsSerializer
  6. class ProjectView(View):
  7. # 查询所有数据
  8. def get(self, request):
  9. projects_obj = Projects.objects.all()
  10. # 1.可以将模型对象以instance关键字来传递参数,同时如果是查询集对象需要设置many=True,如果不是查询集则不需要
  11. # 2.使用序列化器对象的.data属性,获取序列化器之后的数据
  12. serializer_obj = ProjectsSerializer(instance=projects_obj, many=True)
  13. return JsonResponse(serializer_obj.data, safe=False)
  14. def post(self, request):
  15. python_data = json.loads(request.body)
  16. # 1.使用data关键字参数传递字典参数
  17. # 2.可以使用序列化器对象调用.is_valid()方法,才会开始对前端输入的参数进行校验,如果校验通过.is_valid()方法返回True,否则返回False,校验不通过会抛出异常,否则不会抛出异常
  18. # 3.调用.is_valid()方法之后,使用序列化器对象调用.errors属性,来获取错误提示信息
  19. # 4.调用.is_valid()方法之后,使用序列化器对象调用.validated_data属性,来获取校验通过之后的数据,
  20. serializer_obj = ProjectsSerializer(data=python_data)
  21. if not serializer_obj.is_valid():
  22. return JsonResponse(serializer_obj.errors)
  23. project_obj = Projects.objects.create(**serializer_obj.validated_data)
  24. serializer = ProjectsSerializer(instance=project_obj)
  25. return JsonResponse(serializer.data)
  26. class ProjectDetailView(View):
  27. # 查询单个数据
  28. def get(self, request, pk):
  29. try:
  30. project_obj = Projects.objects.get(id=pk)
  31. except Projects.DoesNotExist:
  32. raise Http404
  33. serializer_obj = ProjectsSerializer(instance=project_obj)
  34. return JsonResponse(serializer_obj.data)
  35. # 更新数据
  36. def put(self, request, pk):
  37. update_data = json.loads(request.body)
  38. try:
  39. project_obj = Projects.objects.get(id=pk)
  40. except Projects.DoesNotExist:
  41. raise Http404
  42. # 前端传过来的数据转化为python数据,然后传给ProjectsSerializer 进行校验,如果校验不通过,抛出异常
  43. serializer_obj = ProjectsSerializer(data=update_data)
  44. if not serializer_obj.is_valid():
  45. return JsonResponse(serializer_obj.errors)
  46. project_obj.name = serializer_obj.validated_data.get('name')
  47. project_obj.desc = serializer_obj.validated_data.get('desc')
  48. project_obj.save()
  49. serializer = ProjectsSerializer(instance=project_obj)
  50. return JsonResponse(serializer.data, status=201)
  51. # 删除数据
  52. def delete(self, request, pk):
  53. try:
  54. project_obj = Projects.objects.get(id=pk)
  55. except Projects.DoesNotExist:
  56. raise Http404
  57. project_obj.delete()
  58. return JsonResponse({'msg': '删除成功'})

ok,我们继续用postman测试下。测试过程省略。

优化POST、PUT请求

优化POST请求

我们执行POST请求的时候,实际上我们创建了两个序列化器类对象,一个serializer_obj用于反序列化参数校验(给data传参),一个serializer用于序列化输出(给instance传参),能不能只创建一个序列化器对象就能实现两个对象的功能呢?答案是肯定可以的,我们来优化下代码。

我们直接调用serializer_obj的save()方法,save()方法会自动调用序列化器类定义的create方法,但是我们现在没有定义模型类里面的create方法,所以我们需要定义一下

打开project\serializers.py,添加create方法

  1. from rest_framework import serializers
  2. from .models import Projects
  3. class ProjectsSerializer(serializers.Serializer):
  4. name = serializers.CharField(max_length=200, min_length=2)
  5. desc = serializers.CharField(max_length=200, allow_blank=True)
  6. def create(self, validated_data):
  7. """
  8. 根据提供的验证过的数据创建并返回一个新的`project`实例。
  9. """
  10. return Projects.objects.create(**validated_data)

打开project\views.py,修改post方法

  1. class ProjectView(View):
  2. # ...其余代码省略
  3. def post(self, request):
  4. python_data = json.loads(request.body)
  5. # 1.使用data关键字参数传递字典参数
  6. # 2.可以使用序列化器对象调用.is_valid()方法,才会开始对前端输入的参数进行校验,如果校验通过.is_valid()方法返回True,否则返回False,校验不通过会抛出异常,否则不会抛出异常
  7. # 3.调用.is_valid()方法之后,使用序列化器对象调用.errors属性,来获取错误提示信息
  8. # 4.调用.is_valid()方法之后,使用序列化器对象调用.validated_data属性,来获取校验通过之后的数据,
  9. serializer_obj = ProjectsSerializer(data=python_data)
  10. if not serializer_obj.is_valid():
  11. return JsonResponse(serializer_obj.errors)
  12. serializer_obj.save()
  13. return JsonResponse(serializer_obj.validated_data)

优化PUT请求

我们直接调用serializer_obj的save()方法,save()方法会自动调用序列化器类定义的update方法,update方法需要传入instance和validated_data 2个参数

打开project\serializers.py,添加update方法

  1. class ProjectsSerializer(serializers.Serializer):
  2. # ...其余代码省略
  3. def update(self, instance, validated_data):
  4. """
  5. 根据提供的验证过的数据更新和返回一个已经存在的`project`实例。
  6. """
  7. instance.name = validated_data.get('name')
  8. instance.desc = validated_data.get('desc')
  9. instance.save()
  10. return instance

打开project\views.py,修改put方法

  1. class ProjectDetailView(View):
  2. # ...其余代码省略
  3. # 更新数据
  4. def put(self, request, pk):
  5. update_data = json.loads(request.body)
  6. try:
  7. project_obj = Projects.objects.get(id=pk)
  8. except Projects.DoesNotExist:
  9. raise Http404
  10. # 前端传过来的数据转化为python数据,然后传给ProjectsSerializer 进行校验,如果校验不通过,抛出异常
  11. serializer_obj = ProjectsSerializer(data=update_data, instance=project_obj)
  12. if not serializer_obj.is_valid():
  13. return JsonResponse(serializer_obj.errors)
  14. serializer_obj.save()
  15. return JsonResponse(serializer_obj.validated_data)

ok,我们继续用postman测试下。测试过程省略。
参考:Serializers - Django REST framework (django-rest-framework.org)

DRF validators(校验器)

大多数时候,在REST框架中处理验证时,只需依赖默认的字段验证,然而,有时我们会希望将验证逻辑放入可重用的组件中,以便在整个代码库中轻松重用。这可以通过使用验证器函数和验证器类来实现。有点难理解,我们来写代码慢慢理解。

UniqueValidator(唯一校验)

还记得我们之前models.py 里面name字段吗?

  1. class Projects(models.Model):
  2. name = models.CharField(max_length=200, unique=True)
  3. # ...其余代码省略

name字段里面有个unique=True,就是说我们对name这个字段做了唯一性校验,什么意思呢?就是说我们不能创建2个相同名称的name,我们用postman试下
image-20220315102441411.png
image-20220315102457678.png
是不是发现一个bug?我们不能因为用户输入相同名称后直接给个报错页面不是。而我们今天讲的validators完美解决这个问题。我们来看看怎么实现。

唯一校验UniqueValidator类一般传入2个参数,一个是queryset,需要传一个查询集,一个是message,为自定义的异常校验信息,如果我们想要用validators,那么需要导入,代码如下:

修改project\serializers.py

  1. from rest_framework import serializers, validators
  2. from .models import Projects
  3. class ProjectsSerializer(serializers.Serializer):
  4. name = serializers.CharField(max_length=200, min_length=2, validators=[
  5. validators.UniqueValidator(queryset=Projects.objects.all(), message="项目名字段重复")])
  6. # ...其余代码省略

postman调试下
image-20220315103301048.png

自定义校验器函数

类外定义校验器

修改project\serializers.py

  1. from rest_framework import serializers, validators
  2. from .models import Projects
  3. # 1、类外自定义校验函数,如果校验不通过,抛出('报错信息'),需要将校验函数名放置到validators列表中
  4. def special_character_check(value):
  5. if '*' in value:
  6. raise serializers.ValidationError('项目名称中不能有*')
  7. class ProjectsSerializer(serializers.Serializer):
  8. name = serializers.CharField(max_length=200, min_length=2, validators=[
  9. validators.UniqueValidator(queryset=Projects.objects.all(), message="项目名字段重复"), special_character_check], )
  10. # ...其余代码省略

postman测试
image-20220315110212478.png

类中定义校验器

自定义校验器函数我们是在类外定义了一个方法,同样的也可以在类里面创建一个校验器方法,不同的有以下几点:

  • 方法名必须以validate_作为前缀,后缀为对应的字段名
  • 一定要返回校验之后的值
  • 不需要放在validators的列表中就可以生效

    1. class ProjectsSerializer(serializers.Serializer):
    2. name = serializers.CharField(max_length=200, min_length=2, validators=[
    3. validators.UniqueValidator(queryset=Projects.objects.all(), message="项目名字段重复"), special_character_check], )
    4. def validate_name(self, value):
    5. if '/' in value:
    6. raise serializers.ValidationError("项目名称中不能有/")
    7. return value
    8. # ...其余代码省略
    1. 字段类型进行校验(比如max_length=200 -> 依次验证validators列表中的校验规则(可能有类外定义的校验器) -> 类中定义的校验器

参考:Validators - Django REST framework ~ 验证器 - Django REST 框架 (django-rest-framework.org)

ModelSerializer

我们的ProjectsSerializer类中重复了很多包含在Projects模型类(model)中的信息。如果能保证我们的代码整洁,那就更好了。

就像Django提供了Form类和ModelForm类(我们之前没讲)一样,REST framework包括Serializer类和ModelSerializer类。

我们来看看使用ModelSerializer类重构我们的序列化类,打开serializers.py,添加如下代码

修改**project\serializers.py**

  1. class ProjectsModelSerializer(serializers.ModelSerializer):
  2. class Meta:
  3. model = Projects
  4. # 默认全部字段输入输出
  5. fields = '__all__'
  6. '''
  7. # 指定输入输出字段
  8. fields = ('name', )
  9. # 不需要输出或输入的字段
  10. exclude = ('desc', )
  11. # 不需要输入,只需要输出
  12. read_only_fields = ('update_time',)
  13. # 对name进行拓展
  14. extra_kwargs = {
  15. 'name': {
  16. 'max_length': 50,
  17. 'validators': [validators.UniqueValidator(queryset=Projects.objects.all(), message="项目名字段重复")]
  18. },
  19. }
  20. '''

天啦,就是这么简单,对比之前ProjectsSerializer类是不是简写了很多代码,create和update方法都不需要我们写了,因为ModelSerializer类中自带的有create和update方法
规则:

  • 需要在Meta类中使用model类属性来指定需要按照哪一个模型类来创建
  • fields类属性指定模型类中哪些字段需要输入或输出,fields = '__all__'意思是所有字段都需要输入和输出
  • 默认id主键会添加read_only=True
  • ModelSerializer类中自带的有create和update方法,无需重写即可生效

修改views.py
其实我们只需要把ProjectsSerializer改成ProjectsModelSerializer,但是为了方便复制粘贴,我们还是放上完整代码

  1. import json
  2. from django.http import JsonResponse, Http404
  3. from django.views import View
  4. from .models import Projects
  5. from .serializers import ProjectsModelSerializer
  6. class ProjectView(View):
  7. # 查询所有数据
  8. def get(self, request):
  9. projects_obj = Projects.objects.all()
  10. # 1.可以将模型对象以instance关键字来传递参数,同时如果是查询集对象需要设置many=True,如果不是查询集则不需要
  11. # 2.使用序列化器对象的.data属性,获取序列化器之后的数据
  12. serializer_obj = ProjectsModelSerializer(instance=projects_obj, many=True)
  13. return JsonResponse(serializer_obj.data, safe=False)
  14. def post(self, request):
  15. python_data = json.loads(request.body)
  16. # 1.使用data关键字参数传递字典参数
  17. # 2.可以使用序列化器对象调用.is_valid()方法,才会开始对前端输入的参数进行校验,如果校验通过.is_valid()方法返回True,否则返回False,校验不通过会抛出异常,否则不会抛出异常
  18. # 3.调用.is_valid()方法之后,使用序列化器对象调用.errors属性,来获取错误提示信息
  19. # 4.调用.is_valid()方法之后,使用序列化器对象调用.validated_data属性,来获取校验通过之后的数据,
  20. serializer_obj = ProjectsModelSerializer(data=python_data)
  21. if not serializer_obj.is_valid():
  22. return JsonResponse(serializer_obj.errors)
  23. serializer_obj.save()
  24. return JsonResponse(serializer_obj.validated_data)
  25. class ProjectDetailView(View):
  26. # 查询单个数据
  27. def get(self, request, pk):
  28. try:
  29. project_obj = Projects.objects.get(id=pk)
  30. except Projects.DoesNotExist:
  31. raise Http404
  32. serializer_obj = ProjectsModelSerializer(instance=project_obj)
  33. return JsonResponse(serializer_obj.data)
  34. # 更新数据
  35. def put(self, request, pk):
  36. update_data = json.loads(request.body)
  37. try:
  38. project_obj = Projects.objects.get(id=pk)
  39. except Projects.DoesNotExist:
  40. raise Http404
  41. # 前端传过来的数据转化为python数据,然后传给ProjectsSerializer 进行校验,如果校验不通过,抛出异常
  42. serializer_obj = ProjectsModelSerializer(data=update_data, instance=project_obj)
  43. if not serializer_obj.is_valid():
  44. return JsonResponse(serializer_obj.errors)
  45. serializer_obj.save()
  46. return JsonResponse(serializer_obj.validated_data)
  47. # 删除数据
  48. def delete(self, request, pk):
  49. try:
  50. project_obj = Projects.objects.get(id=pk)
  51. except Projects.DoesNotExist:
  52. raise Http404
  53. project_obj.delete()
  54. return JsonResponse({'msg': '删除成功'})

ok,我们继续用postman测试下。测试过程省略。