在先後端不分離的項目中,可使用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": "張老師" } ] } }