我们之前讲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 serializers
from .models import Projects
class 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 json
from django.http import JsonResponse, Http404
from django.views import View
from .models import Projects
from .serializers import ProjectsSerializer
class 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 Http404
serializer_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 Http404
project_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 serializers
from .models import Projects
class 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, validators
from .models import Projects
class 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, validators
from .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 json
from django.http import JsonResponse, Http404
from django.views import View
from .models import Projects
from .serializers import ProjectsModelSerializer
class 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 Http404
serializer_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 Http404
project_obj.delete()
return JsonResponse({'msg': '删除成功'})
ok,我们继续用postman测试下。测试过程省略。