在先后端不分离的项目中,可使用Django
自带的forms
组件进行数据验证,也可使用Django
自带的序列化组件对模型表数据进行序列化。mysql
那么在先后端分离的项目中,drf
也提供了数据验证与序列化,相比于Django
原生的序列化它更增强大与易用。sql
首先第一步,咱们须要在项目全局文件夹中注册drf
数据库
INSTALLED_APPS = [ 'app01.apps.App01Config', 'rest_framework', # 注册drf ]
下面是项目中的模型表。django
学生和班级是一对多后端
班级和老师是多对多api
班级和班主任是一对一安全
from django.db import models # Create your models here. class Student(models.Model): student_id = models.AutoField(primary_key=True, verbose_name="学生编号") student_name = models.CharField(max_length=8, verbose_name="学生姓名") student_gender = models.BooleanField( choices=([0, "male"], [1, "female"]), default=0, verbose_name="学生性别") student_class = models.ForeignKey( to="Classes", verbose_name="所属班级", on_delete=models.CASCADE) # 一个班级多个学生 def __str__(self): return self.student_name class Meta: db_table = '' managed = True verbose_name = 'Student' verbose_name_plural = 'Students' class Classes(models.Model): class_id = models.AutoField(primary_key=True, verbose_name="班级编号") class_name = models.CharField(max_length=8, verbose_name="班级名称") class_teacher = models.OneToOneField( to="Teacher", verbose_name="班主任", on_delete=models.CASCADE) # 一个班级只有一个班主任 def __str__(self): return self.class_name class Meta: db_table = '' managed = True verbose_name = 'Classes' verbose_name_plural = 'Classess' class Teacher(models.Model): teacher_id = models.AutoField(primary_key=True, verbose_name="教师编号") teacher_name = models.CharField(max_length=8, verbose_name="教师姓名") teacher_class = models.ManyToManyField( to="Classes", verbose_name="任教班级") # 一个班级中可有多个老师,一个老师也能够在多个班级中任教 def __str__(self): return self.teacher_name class Meta: db_table = '' managed = True verbose_name = 'Teacher' verbose_name_plural = 'Teachers'
学生信息以下:app
mysql> select * from app01_student; +------------+--------------+----------------+------------------+ | student_id | student_name | student_gender | student_class_id | +------------+--------------+----------------+------------------+ | 1 | 学生1 | 1 | 1 | | 2 | 学生2 | 1 | 2 | | 3 | 学生3 | 1 | 2 | | 4 | 学生4 | 0 | 1 | +------------+--------------+----------------+------------------+
教师信息以下:前后端分离
mysql> select * from app01_teacher; +------------+--------------+ | teacher_id | teacher_name | +------------+--------------+ | 1 | 王老师 | | 2 | 李老师 | | 3 | 张老师 | +------------+--------------+
班级信息以下:函数
mysql> select * from app01_classes; +----------+--------------+------------------+ | class_id | class_name | class_teacher_id | +----------+--------------+------------------+ | 1 | 高一一班 | 1 | | 2 | 高二一班 | 2 | +----------+--------------+------------------+
教师与班级关系以下:
mysql> select * from app01_teacher_teacher_class; +----+------------+------------+ | id | teacher_id | classes_id | +----+------------+------------+ | 1 | 1 | 1 | | 2 | 1 | 2 | | 3 | 2 | 1 | | 4 | 2 | 2 | | 5 | 3 | 1 | | 6 | 3 | 2 | +----+------------+------------+
接下来书写url
,以查询学生模型表为例,为了符合framework
规范,因此要有一个有名分组来接收可能获取/修改/删除的编号。
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^api/students/(?P<sid>\d+)?', views.Student.as_view()), ] # 添加问号,表明可有,可没有
使用自定义序列器。能够更加灵活的使用序列化及反序列化。也是推荐使用的方式。
序列类的做用以下:
- 序列化时,可选择序列化的模型表字段
- 反序列化时,可选择对数据验证的规则,相似于forms组件
from .models import Student from rest_framework import serializers class StudentSerializer(serializers.Serializer): student_id = serializers.CharField() student_name = serializers.CharField() student_gender = serializers.BooleanField() student_class = serializers.CharField()
接下来进行序列化,首先要书写视图函数。
因为咱们每次都须要返回一个状态码,以及内容包装,因此能够建立一个构建返回内容的类。
第一步要将序列化类导入,在进行序列化的时候将须要序列化的数据对象进行传入。
当序列化完成后,获得一个序列化对象,它有一个data
属性,是已经序列化完成的一个字典。
把字典返回,若是不使用rest_framework
提供的Response
,就得使用JsonResponse
。
序列化单个对象,不须要在序列化时指定参数many
序列化多个对象,须要在序列化时指定参数many
from rest_framework.views import APIView from rest_framework.views import Request from rest_framework.views import Response # 经过Response返回 from app01 import models from app01.drf_ser import StudentSerializer class returnData(object): # 构建返回的信息 def __init__(self): self.status = 100 # 100表明成功,200表明失败 self.message = None # 返回的信息 self.data = None # 序列化后的结果 def get_dict(self): return self.__dict__ @property def data(self): return self.data @data.setter def data(self, value): self.__dict__["data"] = value class Student(APIView): def get(self, request, sid=None): # 初始化返回信息 res = returnData() if sid: # 表明获取单个 obj = models.Student.objects.filter(pk=sid).first() if obj: serializer_result = StudentSerializer(obj) res.data = serializer_result.data else: res.message = "没有该学生" res.status = 200 return Response(res.get_dict()) else: # 表明获取全部 obj_queryset = models.Student.objects.all() if obj_queryset: serializer_result = StudentSerializer( obj_queryset, many=True) # many=True表明获取多条 res.data = serializer_result.data else: res.message = "暂时没有任何学生" res.status = 200 return Response(res.get_dict()) def post(self, request, sid): # 新增 pass def delete(self, request, sid): # 删除 pass def patch(self, request, sid): # 修改 pass
序列化时,只关心怎样将数据返回给页面。
因此咱们只作GET
部分,注意用if
判断来断定是获取全部,仍是获取单个。
如下是获取所有的结果:
# http://127.0.0.1:8000/api/students/ { "status": 100, "message": null, "data": [ { "student_id": "1", "student_name": "学生1", "student_gender": true, "student_class": "高一一班" }, { "student_id": "2", "student_name": "学生2", "student_gender": true, "student_class": "高二一班" }, { "student_id": "3", "student_name": "学生3", "student_gender": true, "student_class": "高二一班" }, { "student_id": "4", "student_name": "学生4", "student_gender": false, "student_class": "高一一班" } ] }
如下是获取单条的结果:
{ "status": 100, "message": null, "data": { "student_id": "1", "student_name": "学生1", "student_gender": true, "student_class": "高一一班" } }
当获取出错时,message
中就会存在错误信息:
{ "status": 200, "message": "没有该学生", "data": null }
反序列化一般是使用POST/PATCH
插入或更新数据时使用,收集到页面传递的数据,进行反序列化后写入数据库中。
当进行反序列化时,能够在序列类中指定一些参数,对将要反序列化写入模型表的字段进行检查。
参数 | 描述 |
---|---|
max_length | 最大长度 |
min_lenght | 最小长度 |
allow_blank | 是否容许为空 |
trim_whitespace | 是否截断空白字符 |
max_value | 最小值 |
min_value | 最大值 |
required | 代表该字段在反序列化时必须输入,默认True |
default | 反序列化时使用的默认值 |
allow_null | 代表该字段是否容许传入None,默认False |
validators | 该字段使用的验证器 |
error_messages | 包含错误编号与错误信息的字典 |
初此以外,还有两个比较特殊的参数:
参数 | 描述 |
---|---|
read_only | 代表该字段仅用于序列化输出,默认False,若是设置成True,postman中能够看到该字段,修改时,不须要传该字段 |
write_only | 代表该字段仅用于反序列化输入,默认False,若是设置成True,postman中看不到该字段,修改时,该字段须要传 |
validate_字段名
是局部钩子
validate
是全局钩子
若是要在钩子中抛出异常,则须要导入异常模块。
from rest_framework import exceptions # raise exceptions.ValidationError('异常了')
以下是局部钩子的使用示例,由于页面提交过来的数据关于一对多中的班级字段是字符串,因此咱们须要将字符串变为模型表对象,方便后面的建立以及更新。
def validate_student_class(self, data): # data是提交过来的这一个字段的数据 class_obj = Classes.objects.filter(class_name=data).first() if not class_obj: raise exceptions.ValidationError("班级不存在") data = class_obj # 将字符串替换为对象 return data
全局钩子使用也是同样。以下,验证学生名和班级名是否相同,若是相同则抛出异常
def validate(self, validate_data): student_name = validate_data.get("student_name") class_obj = validate_data.get("student_class") # 因为局部钩子中,这里被替换成了对象,因此咱们拿到对象不能直接做比较 if student_name == class_obj.class_name: raise exceptions.ValidationError("学生名不能和班级名相同") return validate_data
在进行反序列化时,必须在序列类中覆写create()
方法以及update()
方法。
其中create()
方法针对的是新增用户,而update()
方法针对的是更新用户。
若是不进行覆写,则会抛出异常,这是由于做者在源码中作了接口约束的设置:
def update(self, instance, validated_data): raise NotImplementedError('`update()` must be implemented.') def create(self, validated_data): raise NotImplementedError('`create()` must be implemented.')
了解了上面反序列化须要注意的事项后,开始书写视图函数中的POST/PATCH
方法。
下面是建立一个学生的例子:
def post(self, request): # 初始化返回信息 res = returnData() serializer_result = StudentSerializer(data=request.data) # 传入request.data便可。这里必定要使用关键字传参!!! if serializer_result.is_valid(): # 验证经过了 serializer_result.save() # 保存序列化实例类 res.data = serializer_result.data # 遵循规范,返回新增的数据 else: # 验证没经过 res.status = 200 res.message = "数据校验失败" res.data = serializer_result.errors # 添加错误信息 return Response(res.get_dict())
重写create()
方法并返回:
def create(self, validated_data): instance = Student.objects.create(**validated_data) return instance # 这里返回的信息会返回到序列类对象的data属性中
下面是修改一个学生的例子:
def patch(self, request, sid): # 初始化返回信息 res = returnData() obj = models.Student.objects.filter(pk=sid).first() if obj: serializer_result = StudentSerializer(instance=obj, data=request.data) # 须要传入的参数,记录自己,新的数据 if serializer_result.is_valid(): # 验证经过了 serializer_result.save() # 保存序列化实例类 res.data = serializer_result.data # 遵循规范,返回修改的数据 else: # 验证没经过 res.status = 200 res.message = "数据校验失败" res.data = serializer_result.errors # 添加错误信息 else: res.status = 200 res.message = "用户不存在" return Response(res.get_dict())
重写update()
方法并返回:
def update(self, instance, validated_data): # 对数据作更新后再返回 # validated_data中取出str的键,而后用反射进行设置 for k, v in validated_data.items(): setattr(instance, k, v) instance.save() return instance
views.py
from rest_framework.views import APIView from rest_framework.views import Request from rest_framework.views import Response # 经过Response返回 from app01 import models from app01.drf_ser import StudentSerializer # 导入自定义序列类 class returnData(object): # 构建返回的信息 def __init__(self): self.status = 100 # 100表明成功,200表明失败 self.message = None # 返回的信息 self.data = None # 序列化后的结果 def get_dict(self): return self.__dict__ @property def data(self): return self.data @data.setter def data(self, value): self.__dict__["data"] = value class Student(APIView): def get(self, request, sid=None): # 初始化返回信息 res = returnData() if sid: # 表明获取单个 obj = models.Student.objects.filter(pk=sid).first() if obj: serializer_result = StudentSerializer(obj) res.data = serializer_result.data else: res.message = "没有该学生" res.status = 200 return Response(res.get_dict()) else: # 表明获取全部 obj_queryset = models.Student.objects.all() if obj_queryset: serializer_result = StudentSerializer( obj_queryset, many=True) # many=True表明获取多条 res.data = serializer_result.data else: res.message = "暂时没有任何学生" res.status = 200 return Response(res.get_dict()) def post(self, request): # 初始化返回信息 res = returnData() serializer_result = StudentSerializer( data=request.data) # 传入request.data便可 if serializer_result.is_valid(): # 验证经过了 serializer_result.save() # 保存序列化实例类 res.data = serializer_result.data # 遵循规范,返回新增的数据 else: # 验证没经过 res.status = 200 res.message = "数据校验失败" res.data = serializer_result.errors # 添加错误信息 return Response(res.get_dict()) def delete(self, request): # 删除 pass def patch(self, request, sid): # 初始化返回信息 res = returnData() obj = models.Student.objects.filter(pk=sid).first() if obj: serializer_result = StudentSerializer(instance=obj, data=request.data) # 须要传入的参数,记录自己,新的数据 if serializer_result.is_valid(): # 验证经过了 serializer_result.save() # 保存序列化实例类 res.data = serializer_result.data # 遵循规范,返回修改的数据 else: # 验证没经过 res.status = 200 res.message = "数据校验失败" res.data = serializer_result.errors # 添加错误信息 else: res.status = 200 res.message = "用户不存在" return Response(res.get_dict())
自定义序列类
from .models import Student from .models import Classes from rest_framework import serializers from rest_framework import exceptions class StudentSerializer(serializers.Serializer): student_id = serializers.CharField(read_only=True) # 建立/修改时不用传该字段,可是页面能够看见 # 相反的,若是是wrtie_only则表明页面看不见,可是你要传 student_name = serializers.CharField(max_length=8, min_length=3) student_gender = serializers.BooleanField() student_class = serializers.CharField() def validate_student_class(self, data): class_obj = Classes.objects.filter(class_name=data).first() if not class_obj: raise exceptions.ValidationError("班级不存在") data = class_obj # 将字符串替换为对象 return data def validate(self, validate_data): student_name = validate_data.get("student_name") class_obj = validate_data.get("student_class") if student_name == class_obj.class_name: raise exceptions.ValidationError("学生名不能和班级名相同") return validate_data def create(self, validated_data): instance = Student.objects.create(**validated_data) return instance # 这里返回的信息会返回到序列类对象的data属性中 def update(self, instance, validated_data): # 对数据作更新后再返回 # validated_data中取出str的键,而后用反射进行设置 for k, v in validated_data.items(): setattr(instance, k, v) instance.save() return instance
模型表的序列器定制化比较低,可是使用较为方便。
可以很是快速的开发接口。
建立序列器:
# 模型表序列器 class StudentModelSerializer(serializers.ModelSerializer): student_id = serializers.CharField(read_only=True) # 建立/修改时不用传该字段,可是页面能够看见 student_name = serializers.CharField(max_length=8, min_length=3) student_gender = serializers.BooleanField() student_class = serializers.CharField() class Meta: model = Student # 对应上models.py中的模型 fields = '__all__' # 序列化全部字段 # fields=('student_id','student_name') # 只序列化指定的字段 # exclude=('student_id',) # 跟fields不能同时都写,写谁,就表示排除谁 # read_only_fields=('student_id',) # write_only_fields=('student_class',) # 弃用了,使用extra_kwargs # extra_kwargs = { # 相似于这种形式 student_name=serializers.CharField(max_length=16,min_length=4) # 'student_name': {'write_only': True}, # } def validate_student_class(self, data): class_obj = Classes.objects.filter(class_name=data).first() if not class_obj: raise exceptions.ValidationError("班级不存在") data = class_obj # 将字符串替换为对象 return data def validate(self, validate_data): student_name = validate_data.get("student_name") class_obj = validate_data.get("student_class") print(class_obj) if student_name == class_obj.class_name: raise exceptions.ValidationError("学生名不能和班级名相同") return validate_data
其余使用如出一辙,注意在反序列化时不须要重写create()
和updata()
方法了。
如下是视图API
接口中的代码,只须要把本来使用自定义序列器的地方修改为使用模型表序列器便可。
from rest_framework.views import APIView from rest_framework.views import Request from rest_framework.views import Response # 经过Response返回 from app01 import models # from app01.drf_ser import StudentModelSerializer from app01.drf_ser import StudentModelSerializer class returnData(object): # 构建返回的信息 def __init__(self): self.status = 100 # 100表明成功,200表明失败 self.message = None # 返回的信息 self.data = None # 序列化后的结果 def get_dict(self): return self.__dict__ @property def data(self): return self.data @data.setter def data(self, value): self.__dict__["data"] = value class Student(APIView): def get(self, request, sid=None): # 初始化返回信息 res = returnData() if sid: # 表明获取单个 obj = models.Student.objects.filter(pk=sid).first() if obj: serializer_result = StudentModelSerializer(obj) res.data = serializer_result.data else: res.message = "没有该学生" res.status = 200 return Response(res.get_dict()) else: # 表明获取全部 obj_queryset = models.Student.objects.all() if obj_queryset: serializer_result = StudentModelSerializer( obj_queryset, many=True) # many=True表明获取多条 res.data = serializer_result.data else: res.message = "暂时没有任何学生" res.status = 200 return Response(res.get_dict()) def post(self, request): # 初始化返回信息 res = returnData() serializer_result = StudentModelSerializer( data=request.data) # 传入request.data便可 if serializer_result.is_valid(): # 验证经过了 serializer_result.save() # 保存序列化实例类 res.data = serializer_result.data # 遵循规范,返回新增的数据 else: # 验证没经过 res.status = 200 res.message = "数据校验失败" res.data = serializer_result.errors # 添加错误信息 return Response(res.get_dict()) def delete(self, request): # 删除 pass def patch(self, request, sid): # 初始化返回信息 res = returnData() obj = models.Student.objects.filter(pk=sid).first() if obj: serializer_result = StudentModelSerializer(instance=obj, data=request.data) # 须要传入的参数,记录自己,新的数据 if serializer_result.is_valid(): # 验证经过了 serializer_result.save() # 保存序列化实例类 res.data = serializer_result.data # 遵循规范,返回修改的数据 else: # 验证没经过 res.status = 200 res.message = "数据校验失败" res.data = serializer_result.errors # 添加错误信息 else: res.status = 200 res.message = "用户不存在" return Response(res.get_dict())
source
参数的使用:
.
跨表 cls=serializers.CharField(source='student_class.name') # 至关于 student_student_class__name进行数据获取 该参数最重要的两点,source
中写的是什么,就从哪里取数据,即展现的就是什么。当反序列化时,再也不按照序列器类的字段名进行反序列化,而是按照该参数进行反序列化填值。
看一下,我要让student_name
显示的不是学生的名字,而是班主任的名字,就用到了第一条和第二条,跨表,显示数据,能够这样设置。
class StudentSerializer(serializers.Serializer): student_id = serializers.CharField(read_only=True) # 建立/修改时不用传该字段,可是页面能够看见 student_name = serializers.CharField(max_length=8, min_length=3,source="student_class.class_teacher.teacher_name") # 至关于:Student.objects.filter(pk=传入的id).values_list("student_class__class_teacher__teacher_name")[0][0] student_gender = serializers.BooleanField(source="student_gender") student_class = serializers.CharField()
当进行GET
请求后,将会看到下面的结果:
# http://127.0.0.1:8000/api/students/5/ { "status": 100, "message": null, "data": { "student_id": "5", "student_name": "王老师", # 因此说,该参数后面写的是什么,展现的就是什么。 "student_gender": false, "student_class": "高一一班" } }
示例演示,咱们一般会将展现的数据名字进行重命名,区分开与数据库存储的字段名,这样作更加安全,因此能够进行以下设置:
from .models import Student from .models import Classes from rest_framework import serializers from rest_framework import exceptions class StudentSerializer(serializers.Serializer): sid = serializers.CharField(read_only=True,source="student_id") # 建立/修改时不用传该字段,可是页面能够看见 name = serializers.CharField(max_length=8, min_length=3,source="student_name") gender = serializers.BooleanField(source="student_gender") classes = serializers.CharField(source="student_class") # source中写的是什么,就从哪里取数据 def validate_classes(self, data): # data是提交过来的这一个字段的数据 class_obj = Classes.objects.filter(class_name=data).first() if not class_obj: raise exceptions.ValidationError("班级不存在") data = class_obj # 将字符串替换为对象 return data def create(self, validated_data): instance = Student.objects.create(**validated_data) return instance # 这里返回的信息会返回到序列类对象的data属性中 def update(self, instance, validated_data): # 对数据作更新后再返回 # validated_data中取出str的键,而后用反射进行设置 for k, v in validated_data.items(): setattr(instance, k, v) instance.save() return instance
{ "status": 100, "message": null, "data": { "sid": "5", "name": "修改学生5", "gender": false, "classes": "高一一班" } }
它须要有个配套方法,方法名叫get_字段名
,返回值就是要显示的东西。
好比,咱们想查看每一个学生都有那些老师在教授,就可使用该参数:
class StudentSerializer(serializers.Serializer): sid = serializers.CharField(read_only=True,source="student_id") # 建立/修改时不用传该字段,可是页面能够看见 name = serializers.CharField(max_length=8, min_length=3,source="student_name") gender = serializers.BooleanField(source="student_gender") classes = serializers.CharField(source="student_class") # 如今,我要让他显示的是班级编号,而再也不是班级名称了 students = serializers.SerializerMethodField() def get_students(self,instance): teacher_queryset = instance.student_class.teacher_set.values("pk","teacher_name") return teacher_queryset
最后的结果以下:
# http://127.0.0.1:8000/api/students/5/ { "status": 100, "message": null, "data": { "sid": "5", "name": "修改学生5", "gender": false, "classes": "高一一班", "students": [ { "pk": 1, "teacher_name": "王老师" }, { "pk": 2, "teacher_name": "李老师" }, { "pk": 3, "teacher_name": "张老师" } ] } }