1. 程式人生 > 實用技巧 >drf之序列化元件(一):Serializer

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中。