我们之前讲django类视图操作数据库时,给大家提到过序列化和反序列化,我们再来把代码扒下来看下
class ProjectView(View):# 查询所有数据def get(self, request):projects = Projects.objects.all()p = []for i in projects:print(i.name)p.append({'name': i.name})return JsonResponse(p, safe=False)# 创建数据def post(self, request):python_data = json.loads(request.body)project_obj = Projects.objects.create(name=python_data['name'])python_dict = {'name': project_obj.name,}return JsonResponse(python_dict, status=201)
序列化
我们查询所有数据的时候,把模型对象转换son字符串(完整步骤:1. 模型对象转化为python里面的基本类型 2. 把python基本数据类型转换为json字符串)
反序列化
我们创建数据的时候,其实经历了反序列化和序列化的2个过程。
- 将json格式数据转换为模型对象(反序列化)(完整步骤:1. json参数并转化为python中的数据类型2.把python中的数据类型转化为模型对象)
- 然后把模型对象转换son字符串(序列化输出)
其实这样转来转去的还是蛮复杂的,DRF 中有个serializers 序列化器完美帮我们解决了这个问题。
serializers 序列化器
首先我们在project 下新建serializers.py文件,并添加以下内容
from rest_framework import serializersfrom .models import Projectsclass ProjectsSerializer(serializers.Serializer):name = serializers.CharField(max_length=200, min_length=2)desc = serializers.CharField(max_length=200, allow_blank=True)
序列化器类的第一部分定义了序列化/反序列化的字段(models.py)。
序列化器类与Django Form类非常相似,并在各种字段中包含类似的验证标志,例如required,max_length和default。
修改project\views.py文件
import jsonfrom django.http import JsonResponse, Http404from django.views import Viewfrom .models import Projectsfrom .serializers import ProjectsSerializerclass ProjectView(View):# 查询所有数据def get(self, request):projects_obj = Projects.objects.all()# 1.可以将模型对象以instance关键字来传递参数,同时如果是查询集对象需要设置many=True,如果不是查询集则不需要# 2.使用序列化器对象的.data属性,获取序列化器之后的数据serializer_obj = ProjectsSerializer(instance=projects_obj, many=True)return JsonResponse(serializer_obj.data, safe=False)def post(self, request):python_data = json.loads(request.body)# 1.使用data关键字参数传递字典参数# 2.可以使用序列化器对象调用.is_valid()方法,才会开始对前端输入的参数进行校验,如果校验通过.is_valid()方法返回True,否则返回False,校验不通过会抛出异常,否则不会抛出异常# 3.调用.is_valid()方法之后,使用序列化器对象调用.errors属性,来获取错误提示信息# 4.调用.is_valid()方法之后,使用序列化器对象调用.validated_data属性,来获取校验通过之后的数据,serializer_obj = ProjectsSerializer(data=python_data)if not serializer_obj.is_valid():return JsonResponse(serializer_obj.errors)project_obj = Projects.objects.create(**serializer_obj.validated_data)serializer = ProjectsSerializer(instance=project_obj)return JsonResponse(serializer.data)class ProjectDetailView(View):# 查询单个数据def get(self, request, pk):try:project_obj = Projects.objects.get(id=pk)except Projects.DoesNotExist:raise Http404serializer_obj = ProjectsSerializer(instance=project_obj)return JsonResponse(serializer_obj.data)# 更新数据def put(self, request, pk):update_data = json.loads(request.body)try:project_obj = Projects.objects.get(id=pk)except Projects.DoesNotExist:raise Http404# 前端传过来的数据转化为python数据,然后传给ProjectsSerializer 进行校验,如果校验不通过,抛出异常serializer_obj = ProjectsSerializer(data=update_data)if not serializer_obj.is_valid():return JsonResponse(serializer_obj.errors)project_obj.name = serializer_obj.validated_data.get('name')project_obj.desc = serializer_obj.validated_data.get('desc')project_obj.save()serializer = ProjectsSerializer(instance=project_obj)return JsonResponse(serializer.data, status=201)# 删除数据def delete(self, request, pk):try:project_obj = Projects.objects.get(id=pk)except Projects.DoesNotExist:raise Http404project_obj.delete()return JsonResponse({'msg': '删除成功'})
优化POST、PUT请求
优化POST请求
我们执行POST请求的时候,实际上我们创建了两个序列化器类对象,一个serializer_obj用于反序列化参数校验(给data传参),一个serializer用于序列化输出(给instance传参),能不能只创建一个序列化器对象就能实现两个对象的功能呢?答案是肯定可以的,我们来优化下代码。
我们直接调用serializer_obj的save()方法,save()方法会自动调用序列化器类定义的create方法,但是我们现在没有定义模型类里面的create方法,所以我们需要定义一下
打开project\serializers.py,添加create方法
from rest_framework import serializersfrom .models import Projectsclass ProjectsSerializer(serializers.Serializer):name = serializers.CharField(max_length=200, min_length=2)desc = serializers.CharField(max_length=200, allow_blank=True)def create(self, validated_data):"""根据提供的验证过的数据创建并返回一个新的`project`实例。"""return Projects.objects.create(**validated_data)
打开project\views.py,修改post方法
class ProjectView(View):# ...其余代码省略def post(self, request):python_data = json.loads(request.body)# 1.使用data关键字参数传递字典参数# 2.可以使用序列化器对象调用.is_valid()方法,才会开始对前端输入的参数进行校验,如果校验通过.is_valid()方法返回True,否则返回False,校验不通过会抛出异常,否则不会抛出异常# 3.调用.is_valid()方法之后,使用序列化器对象调用.errors属性,来获取错误提示信息# 4.调用.is_valid()方法之后,使用序列化器对象调用.validated_data属性,来获取校验通过之后的数据,serializer_obj = ProjectsSerializer(data=python_data)if not serializer_obj.is_valid():return JsonResponse(serializer_obj.errors)serializer_obj.save()return JsonResponse(serializer_obj.validated_data)
优化PUT请求
我们直接调用serializer_obj的save()方法,save()方法会自动调用序列化器类定义的update方法,update方法需要传入instance和validated_data 2个参数
打开project\serializers.py,添加update方法
class ProjectsSerializer(serializers.Serializer):# ...其余代码省略def update(self, instance, validated_data):"""根据提供的验证过的数据更新和返回一个已经存在的`project`实例。"""instance.name = validated_data.get('name')instance.desc = validated_data.get('desc')instance.save()return instance
打开project\views.py,修改put方法
class ProjectDetailView(View):# ...其余代码省略# 更新数据def put(self, request, pk):update_data = json.loads(request.body)try:project_obj = Projects.objects.get(id=pk)except Projects.DoesNotExist:raise Http404# 前端传过来的数据转化为python数据,然后传给ProjectsSerializer 进行校验,如果校验不通过,抛出异常serializer_obj = ProjectsSerializer(data=update_data, instance=project_obj)if not serializer_obj.is_valid():return JsonResponse(serializer_obj.errors)serializer_obj.save()return JsonResponse(serializer_obj.validated_data)
ok,我们继续用postman测试下。测试过程省略。
参考:Serializers - Django REST framework (django-rest-framework.org)
DRF validators(校验器)
大多数时候,在REST框架中处理验证时,只需依赖默认的字段验证,然而,有时我们会希望将验证逻辑放入可重用的组件中,以便在整个代码库中轻松重用。这可以通过使用验证器函数和验证器类来实现。有点难理解,我们来写代码慢慢理解。
UniqueValidator(唯一校验)
还记得我们之前models.py 里面name字段吗?
class Projects(models.Model):name = models.CharField(max_length=200, unique=True)# ...其余代码省略
name字段里面有个unique=True,就是说我们对name这个字段做了唯一性校验,什么意思呢?就是说我们不能创建2个相同名称的name,我们用postman试下

是不是发现一个bug?我们不能因为用户输入相同名称后直接给个报错页面不是。而我们今天讲的validators完美解决这个问题。我们来看看怎么实现。
唯一校验UniqueValidator类一般传入2个参数,一个是queryset,需要传一个查询集,一个是message,为自定义的异常校验信息,如果我们想要用validators,那么需要导入,代码如下:
修改project\serializers.py
from rest_framework import serializers, validatorsfrom .models import Projectsclass ProjectsSerializer(serializers.Serializer):name = serializers.CharField(max_length=200, min_length=2, validators=[validators.UniqueValidator(queryset=Projects.objects.all(), message="项目名字段重复")])# ...其余代码省略
自定义校验器函数
类外定义校验器
修改project\serializers.py
from rest_framework import serializers, validatorsfrom .models import Projects# 1、类外自定义校验函数,如果校验不通过,抛出('报错信息'),需要将校验函数名放置到validators列表中def special_character_check(value):if '*' in value:raise serializers.ValidationError('项目名称中不能有*')class ProjectsSerializer(serializers.Serializer):name = serializers.CharField(max_length=200, min_length=2, validators=[validators.UniqueValidator(queryset=Projects.objects.all(), message="项目名字段重复"), special_character_check], )# ...其余代码省略
类中定义校验器
自定义校验器函数我们是在类外定义了一个方法,同样的也可以在类里面创建一个校验器方法,不同的有以下几点:
- 方法名必须以validate_作为前缀,后缀为对应的字段名
- 一定要返回校验之后的值
不需要放在validators的列表中就可以生效
class ProjectsSerializer(serializers.Serializer):name = serializers.CharField(max_length=200, min_length=2, validators=[validators.UniqueValidator(queryset=Projects.objects.all(), message="项目名字段重复"), special_character_check], )def validate_name(self, value):if '/' in value:raise serializers.ValidationError("项目名称中不能有/")return value# ...其余代码省略
字段类型进行校验(比如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**
class ProjectsModelSerializer(serializers.ModelSerializer):class Meta:model = Projects# 默认全部字段输入输出fields = '__all__''''# 指定输入输出字段fields = ('name', )# 不需要输出或输入的字段exclude = ('desc', )# 不需要输入,只需要输出read_only_fields = ('update_time',)# 对name进行拓展extra_kwargs = {'name': {'max_length': 50,'validators': [validators.UniqueValidator(queryset=Projects.objects.all(), message="项目名字段重复")]},}'''
天啦,就是这么简单,对比之前ProjectsSerializer类是不是简写了很多代码,create和update方法都不需要我们写了,因为ModelSerializer类中自带的有create和update方法
规则:
- 需要在Meta类中使用model类属性来指定需要按照哪一个模型类来创建
- fields类属性指定模型类中哪些字段需要输入或输出,
fields = '__all__'意思是所有字段都需要输入和输出 - 默认id主键会添加read_only=True
- ModelSerializer类中自带的有create和update方法,无需重写即可生效
修改views.py
其实我们只需要把ProjectsSerializer改成ProjectsModelSerializer,但是为了方便复制粘贴,我们还是放上完整代码
import jsonfrom django.http import JsonResponse, Http404from django.views import Viewfrom .models import Projectsfrom .serializers import ProjectsModelSerializerclass ProjectView(View):# 查询所有数据def get(self, request):projects_obj = Projects.objects.all()# 1.可以将模型对象以instance关键字来传递参数,同时如果是查询集对象需要设置many=True,如果不是查询集则不需要# 2.使用序列化器对象的.data属性,获取序列化器之后的数据serializer_obj = ProjectsModelSerializer(instance=projects_obj, many=True)return JsonResponse(serializer_obj.data, safe=False)def post(self, request):python_data = json.loads(request.body)# 1.使用data关键字参数传递字典参数# 2.可以使用序列化器对象调用.is_valid()方法,才会开始对前端输入的参数进行校验,如果校验通过.is_valid()方法返回True,否则返回False,校验不通过会抛出异常,否则不会抛出异常# 3.调用.is_valid()方法之后,使用序列化器对象调用.errors属性,来获取错误提示信息# 4.调用.is_valid()方法之后,使用序列化器对象调用.validated_data属性,来获取校验通过之后的数据,serializer_obj = ProjectsModelSerializer(data=python_data)if not serializer_obj.is_valid():return JsonResponse(serializer_obj.errors)serializer_obj.save()return JsonResponse(serializer_obj.validated_data)class ProjectDetailView(View):# 查询单个数据def get(self, request, pk):try:project_obj = Projects.objects.get(id=pk)except Projects.DoesNotExist:raise Http404serializer_obj = ProjectsModelSerializer(instance=project_obj)return JsonResponse(serializer_obj.data)# 更新数据def put(self, request, pk):update_data = json.loads(request.body)try:project_obj = Projects.objects.get(id=pk)except Projects.DoesNotExist:raise Http404# 前端传过来的数据转化为python数据,然后传给ProjectsSerializer 进行校验,如果校验不通过,抛出异常serializer_obj = ProjectsModelSerializer(data=update_data, instance=project_obj)if not serializer_obj.is_valid():return JsonResponse(serializer_obj.errors)serializer_obj.save()return JsonResponse(serializer_obj.validated_data)# 删除数据def delete(self, request, pk):try:project_obj = Projects.objects.get(id=pk)except Projects.DoesNotExist:raise Http404project_obj.delete()return JsonResponse({'msg': '删除成功'})
ok,我们继续用postman测试下。测试过程省略。
