drf之序列化元件(一):Serializer
序列化元件:Serializer、ModelSerializer、ListModelSerializer
Serializer 偏底層ModelSerializer 重點ListModelSerializer 輔助群改
1.Serializer元件
(1)準備
(1)models.py中
class User(models.Model):
SEX_CHOICES = [
[0,”男”], #前面是0還是”0”和IntergerField還是CharField有關
[1,”女”] #前面是填入的,後面是顯示的]
name = models.CharField(max_length=64)
pwd = models.CharField(max_length=32)
phone = models.CharField(max_length=11, null=True, default=None)
sex = models.IntegerField(choices=SEX_CHOICES, default=0)
icon = models.ImageField(upload_to=”icon”, defult=’icon/defult.png’) #需要在專案base目錄下新建media資料夾,然後在media下建立icon資料夾(預設會找media下的icon資料夾)
class Meta:
db_table = ‘yq_user’
verbose_name = “使用者表”
verbose_name_plural = verbose_name
def __str__(self):
return “%s”%self.name
(2)在admin.py下注冊User
admin.site.register(models.User)
(3)在shell中遷移
makemigrations、migrate
get請求是要序列化,將後臺資料返回給前臺;post請求要反序列化,將前臺資料給後臺存入資料庫中,同時也要序列化物件展示資料給前臺。
(4)配置媒體資源
1)在settings.py裡配置media路徑
MEDIA_ROOT = os.path.join(BASE_DIR, ‘media’)
2)在專案檔案下的urls.py中
#media在根路由配置
from django.views.static import serve
from django.conf import setting urlpatterns = [
...
url(r”^media/(?P<path>.*)”, serve, {‘document_root’: setting.MEDIA_ROOT}) #正則匹配時不要寫$,可以一層層找
]
3)資料庫再遷移下
使用者物件(model物件或者query_set物件)、使用者物件列表不能直接返回給前臺,要序列化下物件。因此要用Serializer。
(2)序列化
(1)配置路由
urlpatterns = [
url(r'^users/$', views.User.as_view()),
url(r'^users/(?P<pk>.*)/$', views.User.as_view()),
]
(2)自定義序列化類。在應用資料夾下新建serializers.py檔案。
序列化元件的工作方式與django的forms元件相似
from rest_framework import serializers class UserSerailizer(serializers.Serializer): #欄位的型別和定義時一致,欄位為想要返回給前臺的內容(pwd不用返回就不寫)
name = serializers.CharField() #序列化的括號裡不需加條件
phone = serlializers.CharField()
#sex = serializers.IntegerField()
#icon = serializers.ImageField() #由於sex預設返回的是0、1數字給前臺,icon返回的不是全連結,因此要自定義序列化屬性:定製返回的內容 gender = serializers.SerializerMethodField() #可以叫gender,也可以叫sex,不一定與欄位名保持一致 def get_gender(self, obj): #必須以get開頭,後面也必須和上面的gender對應
#self是serializer物件,obj是模型類物件
return obj.get_sex_display() #get_xxx_display() 表示取到選項值 #在settings.py中配置下MEDIA_URL = /media/
from untitled1 import settings
img = serializer.SerializerMethodField()
def get_img(self, obj):
return “%s%s%s”%(r”http://127.0.0.1:8000”, settings.MEDIA_URL, str(obj.icon))
#obj.icon不能直接作為資料返回,型別是ImageFile型別,可以通過str轉化為str型別。
(3)在views.py中返回物件序列化後的json資料
from . import serializers
from rest_framework.views import APIView
from rest_framework.response import Response class User(APIView): def get(self, request, *args, **kwargs):
pk = kwargs.get('pk') #通過有名分組匹配到的路徑引數
if pk: #單查
try:
user_obj = models.User.objects.get(pk=pk) #取到model物件
#將物件序列化取得dict資料
user_ser_data = serializers.UserSerializer(user_obj).data
return Response({
"status": 0,
"msg": "ok",
"results": user_ser_data
})
except:
return Response({
"status": 2,
"msg": "no user",
})
else: #群查
user_obj_list = models.User.objects.all()
#將物件列表序列化
user_ser_data = serializers.UserSerializer(
user_obj_list,many=True).data
return Response({
"status": 0,
"msg": "ok",
"results":user_ser_data
})
總結:
序列化ser:
1)序列化給前臺的欄位個數可以由後臺自己決定,但是提供的序列化欄位的一定要和資料庫的欄位的型別和名字一致。
2)自定義屬性名:為了返回自定義的欄位的值。屬性名隨意,值由固定的命名規範方法提供:get_屬性名(self, obj)。obj為參與序列化的model或者query_set物件。返回值就是自定義序列化屬性的值。
view:
1)從資料庫中將要序列化給前臺的model物件,或是多個model物件查詢出來
user_obj = models.User.objects.get(pk=pk)或者
user_objs_list = models.User.objects.all()
2)將物件交給序列化處理,產生序列化物件,如果序列化的是多個資料,要設定many=True
user_ser = serializers.UserSerializer(user_obj)或者
user_ser_list = serializers.UserSerializer(user_objs_list, many=True)
3)序列化物件.data就是可以返回給前臺的序列化資料,然後return Response返回
(3)反序列化
反序列化考慮的問題:
1)哪些欄位必須反序列化
2)欄位都有哪些安全校驗
3)哪些欄位需要提供額外的安全校驗
4)哪些欄位間存在聯合校驗
反序列化欄位全都是用來入庫的,不會出現自定義方法屬性,但會出現設定校驗規則(系統校驗、區域性鉤子和全域性鉤子)的自定義屬性(如re_pwd)
(1)還是在剛才的serializers.py檔案中
class UserDeserializer(serializer.Serializer): name = serializers.CharField(
required=True,
max_length=64, #自定義檢驗規則
min_length=3,
error_message = { #錯誤資訊,不給是預設的
‘max_length’:’太長’,
‘min_length’:’太短’,
})
pwd= serializers.CharField(required=True)
phone = serializers.CharField(required=False)
sex = serializers.IntegerField(required=False) #自定義有校驗規則的反序列化欄位。不存入庫,只是用來校驗
re_pwd = serializers.CharField(required=True) #用來判斷兩次輸入密碼一樣 #區域性鉤子:validate_要校驗的欄位名(self, 當前要檢驗欄位的值)
#校驗規則:校驗成功返回原值,失敗返回異常
#區域性鉤子校驗某個欄位
def validate_name(self, value):
if ‘j’ in value.lower(): #名字中不能出現j
#from rest_framework import exceptions
raise exceptions.ValidationError(“名字非法”)
return value #必須返回,校驗成功返回value,失敗拋異常 #全域性鉤子:固定寫法validate(self, attrs)。attrs為系統與區域性鉤子校驗通過的所有資料。(系統和全域性之間還有個區域性鉤子)
#校驗時需要用到多個欄位。比如兩次密碼輸入一致
#校驗規則:校驗成功返回原值,失敗返回異常
def validate(self, attrs):
pwd = attrs.get(‘pwd’) #只是取得
re_pwd = attrs.pop(‘re_pwd’) #取得並且刪除(不需要入庫)
if pwd != re_pwd:
raise exceptions.ValidationError({“pwd“: “兩次密碼不一致”})
return attrs #必須返回,校驗成功返回attrs,失敗拋異常 #要完成新增,需在serializers.py中的自定義反序列化類中重新create方法。
def create(self, validated_data): #在views.py中校驗後,save方法呼叫時內部會呼叫create。
#在所有校驗規則完畢之後,資料(validated_data是已經校驗好的資料)可以直接入庫
#from . import models
return models.User.objects.create(**validated_data)
(2)在views.py下 的User類下寫post方法(對post提交的資料反序列化)
def post(self, request, *args, **kwargs):
request_data = request.data #字典
#資料是否合法(增加物件必須是一個字典資料且有值)
if not isinstance(request_data, dict) or not request_data:
return Response({
"status": 1,
"msg": "資料有誤",
})
#資料型別合法,但資料內容不一定合法,需要校驗資料,校驗的(參與反序列化)資料需要賦值給data
book_ser = serializers.UserDeserializer(data=request_data) #必須關鍵字data賦值
#序列化物件呼叫is_valid完成校驗,校驗失敗資訊儲存在序列化物件.errors中
if book_ser.is_valid():
book_obj = book_ser.save() #校驗通過,完成新增(save的返回值就是完成校驗後create返回的物件)
return Response({
"status": 0,
"msg": "ok",
"results":serializers.UserSerializer(book_obj).data
#新增和修改,都要 將反序列化的物件 再序列化 返回給其前臺
})
else: #校驗沒通過(校驗的錯誤資訊存在book_ser的errors中)
return Response({
"status": 1,
"msg": book_ser.errors,
})
反序列化總結:
檢視類中:
1)把要校驗的資料交給序列化類,由data接收;
book_ser = serializers.UserDeserializer(data=request_data)
2)走is_valid校驗;
if book_ser.is_valid():
3)校驗成功走save方法,校驗失敗返回errors。
book_obj = book_ser.save()
序列化類中:
1)設定必填與選填序列化欄位,設定校驗規則
2)為需要額外校驗的欄位提供區域性鉤子函式,如果某些欄位(如驗證碼)不入庫且不參與全域性鉤子,可以將值取出pop校驗(一般在全域性鉤子裡)
3)為有聯合關係的欄位們提供全域性鉤子。如果某些欄位不入庫,也取出pop校驗(如二次輸入的密碼)
4)重寫create方法,完成校驗通過的欄位入庫,得到新增的物件。
ser = 自定義序列化類(instance=模型類物件, data=empty, **kwargs) #序列化時傳入模型類物件(instance=可以不寫,直接寫物件),不需要寫data(data是反序列化才有);**kwargs比如當物件是個列表時,可以加個many=True引數。
ser = 自定義反序列化類(instance=None, data=資料) #將要被反序列化的前端傳來的資料傳入data中。