django-rest-framework(概念篇)——serializers

https://juejin.im/post/5a68934551882573443cddf8

Serializer的作用:

  • 將queryset與model實(shí)例等進(jìn)行序列化躯畴,轉(zhuǎn)化成json格式,返回給用戶(api接口)
  • 將post與patch/put的上來的數(shù)據(jù)進(jìn)行驗(yàn)證
  • 對post與patch/put數(shù)據(jù)進(jìn)行處理

簡單來說矛物,針對get來說,serializers的作用體現(xiàn)在第一條跪但,但如果是其他請求泽谨,serializers能夠發(fā)揮2,3條的作用。


Save Instance

如果只是簡單的get請求特漩,那么在設(shè)置了前面的field可能就能夠滿足這個(gè)需求吧雹。

我們在view以及mixins的博客中提及到,post請求對應(yīng)create方法涂身,而patch請求對應(yīng)update方法雄卷,這里提到的create方法與update方法,是指mixins中特定類中的方法蛤售。我們看一下源代碼丁鹉,源代碼具體分析可以看到另外一篇博客mixins

# 只截取一部分
class CreateModelMixin(object):
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

class UpdateModelMixin(object):
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

可以看出,無論是create與update都寫了一行:serializer.save( )悴能,那么揣钦,這一行,到底做了什么事情漠酿,分析一下源碼冯凹。

# serializer.py
def save(self, **kwargs):
# 略去一些稍微無關(guān)的內(nèi)容
    ···
    if self.instance is not None:
        self.instance = self.update(self.instance, validated_data)
            ···
    else:
        self.instance = self.create(validated_data)
            ···
    return self.instance

顯然,serializer.save的操作炒嘲,它去調(diào)用了serializer的create或update方法宇姚,不是mixins中的P偻ァ!浑劳!我們看一下流程圖(以post為例)

CreateModelMixins執(zhí)行邏輯

如果你的viewset含有post阱持,那么你需要重載create方法,如果含有patch魔熏,那么就需要重載update方法衷咽。

Validation自定義驗(yàn)證邏輯

  • 單獨(dú)的Validate
mobile_phone = serializers.CharField(max_length=11, min_length=11)

def validate_mobile_phone(self, mobile_phone):
    # 注意參數(shù),self以及字段名
    # 注意函數(shù)名寫法蒜绽,validate_ + 字段名字
    if not re.match(REGEX_MOBILE, mobile):
    # REGEX_MOBILE表示手機(jī)的正則表達(dá)式
        raise serializers.ValidationError("手機(jī)號(hào)碼非法")
    return mobile_phone
  • 聯(lián)合Validate
 start = serializers.DateTimeField()
    finish = serializers.DateTimeField()
    
    def validate(self, attrs):
    # 傳進(jìn)來什么參數(shù)镶骗,就返回什么參數(shù),一般情況下用attrs
        if attrs['start'] > attrs['finish']:
            raise serializers.ValidationError("finish must occur after start")
        return attrs

這個(gè)方法非常的有用滓窍,我們還可以再這里對一些read_only的字段進(jìn)行操作卖词,我們在read_only提及到一個(gè)例子巩那,訂單號(hào)的生成吏夯,我們可以在這步生成一個(gè)訂單號(hào),然后添加到attrs這個(gè)字典中即横。

order_sn = serializers.CharField(readonly=True)
def validate(self, attrs):
    # 調(diào)用一個(gè)方法生成order_sn
    attrs['order_sn'] = generate_order_sn()
    return attrs

這個(gè)方法運(yùn)用在modelserializer中噪生,可以剔除掉write_only的字段,這個(gè)字段只驗(yàn)證东囚,但不存在與指定的model當(dāng)中跺嗽,即不能save( ),可以在這delete掉页藻!

 def validate(self, attrs):
        del attrs["code"]
        return attrs
  • Validator
    validators可以直接作用于某個(gè)字段桨嫁,這個(gè)時(shí)候,它與單獨(dú)的validate作用差不多
def multiple_of_ten(value):
    if value % 10 != 0:
        raise serializers.ValidationError('Not a multiple of ten')

class GameRecord(serializers.Serializer):
    score = IntegerField(validators=[multiple_of_ten])

當(dāng)然份帐,drf提供的validators還有很好的功能:UniqueValidator璃吧,UniqueTogetherValidator等。
UniqueValidator: 指定某一個(gè)對象是唯一的废境,如畜挨,用戶名只能存在唯一:

username = serializers.CharField(
        max_length=11, 
        min_length=11,
        validators=[UniqueValidator(queryset=UserProfile.objects.all())
    )

UniqueTogetherValidator: 聯(lián)合唯一,如用戶收藏某個(gè)課程噩凹,這個(gè)時(shí)候就不能單獨(dú)作用于某個(gè)字段巴元,我們在Meta中設(shè)置。

    class Meta:
        validators = [
            UniqueTogetherValidator(
                queryset=UserFav.objects.all(),
                fields=('user', 'course'),
                message='已經(jīng)收藏'
            )]

ModelSerializer

ModelSerializer在Meta中設(shè)置fields字段驮宴,系統(tǒng)會(huì)自動(dòng)進(jìn)行映射逮刨,省去每個(gè)字段再寫一個(gè)field。

class UserDetailSerializer(serializers.ModelSerializer):
    """
    用戶詳情序列化
    """

    class Meta:
        model = User
        fields = ("name", "gender", "birthday", "email", "mobile")
        # fields = '__all__': 表示所有字段
        # exclude = ('add_time',):  除去指定的某些字段
        # 這三種方式堵泽,存在一個(gè)即可

ModelSerializer需要解決兩個(gè)問題

    1. 某個(gè)字段不屬于指定model禀忆,它是write_only臊旭,需要用戶傳進(jìn)來,但我們不能對它進(jìn)行save( )箩退,因?yàn)镸odelSerializer是基于Model离熏,這個(gè)字段在Model中沒有對應(yīng),這個(gè)時(shí)候戴涝,我們需要重載validate滋戳!
      如在用戶注冊時(shí),我們需要填寫驗(yàn)證碼啥刻,這個(gè)驗(yàn)證碼只需要驗(yàn)證奸鸯,不需要保存到用戶這個(gè)Model中:
def validate(self, attrs):
        del attrs["code"]
        return attrs
    1. 某個(gè)字段不屬于指定model,它是read_only可帽,只需要將它序列化傳遞給用戶娄涩,但是在這個(gè)model中,沒有這個(gè)字段映跟!我們需要用到SerializerMethodField蓄拣。
      假設(shè)需要返回用戶加入這個(gè)網(wǎng)站多久了,不可能維持這樣加入的天數(shù)這樣一個(gè)數(shù)據(jù)努隙,一般會(huì)記錄用戶加入的時(shí)間點(diǎn)球恤,然后當(dāng)用戶獲取這個(gè)數(shù)據(jù),我們再計(jì)算返回給它荸镊。
class UserSerializer(serializers.ModelSerializer):  
    days_since_joined = serializers.SerializerMethodField()
    # 方法寫法:get_ + 字段
    def get_days_since_joined(self, obj):
    # obj指這個(gè)model的對象
        return (now() - obj.date_joined).days
        
    class Meta:
        model = User

關(guān)于外鍵的Serializers

假設(shè)現(xiàn)在有一門課python入門教學(xué)(course)咽斧,它的類別是python(catogory)。

ModelSerializer直接通過映射就可以獲得的一個(gè)外鍵類別的id躬存,但并不能獲取到外鍵Model實(shí)例詳細(xì)的信息张惹,如果想要獲取到具體信息,那需要嵌套serializer

category = CourseCategorySerializer()

以上外鍵都是正向取得岭洲,下面介紹怎么反向去取宛逗,如,我們需要獲取python這個(gè)類別下有什么課程钦椭。首先拧额,在課程course的model中,需要在外鍵中設(shè)置related_name:

class Course(model.Model):
    category = models.ForeignKey(CourseCategory, related_name='courses')
# 反向取課程彪腔,通過related_name
# 一對多侥锦,一個(gè)類別下有多個(gè)課程,一定要設(shè)定many=True
courses = CourseSerializer(many=True)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末德挣,一起剝皮案震驚了整個(gè)濱河市恭垦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖番挺,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件唠帝,死亡現(xiàn)場離奇詭異,居然都是意外死亡玄柏,警方通過查閱死者的電腦和手機(jī)襟衰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粪摘,“玉大人瀑晒,你說我怎么就攤上這事∨且猓” “怎么了苔悦?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長椎咧。 經(jīng)常有香客問我玖详,道長,這世上最難降的妖魔是什么勤讽? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任蟋座,我火速辦了婚禮,結(jié)果婚禮上地技,老公的妹妹穿的比我還像新娘蜈七。我一直安慰自己秒拔,他們只是感情好莫矗,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著砂缩,像睡著了一般作谚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上庵芭,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天妹懒,我揣著相機(jī)與錄音,去河邊找鬼双吆。 笑死眨唬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的好乐。 我是一名探鬼主播匾竿,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蔚万!你這毒婦竟也來了岭妖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎昵慌,沒想到半個(gè)月后假夺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡斋攀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年已卷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淳蔼。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悼尾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肖方,到底是詐尸還是另有隱情闺魏,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布俯画,位于F島的核電站析桥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏艰垂。R本人自食惡果不足惜泡仗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望猜憎。 院中可真熱鬧娩怎,春花似錦、人聲如沸胰柑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柬讨。三九已至崩瓤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間踩官,已是汗流浹背却桶。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蔗牡,地道東北人颖系。 一個(gè)月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像辩越,于是被迫代替她去往敵國和親嘁扼。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Django: csrf防御機(jī)制 csrf攻擊過程 1.用戶C打開瀏覽器区匣,訪問受信任網(wǎng)站A偷拔,輸入用戶名和密碼請求登...
    lijun_m閱讀 1,053評論 0 0
  • API Reference GenericAPIView This class extends REST fram...
    陽光小鎮(zhèn)少爺閱讀 2,582評論 0 1
  • Serializers 序列化器允許將諸如查詢集和模型實(shí)例之類的復(fù)雜數(shù)據(jù)轉(zhuǎn)換為原生 Python 數(shù)據(jù)類型蒋院,然后可...
    lkning閱讀 1,020評論 0 1
  • 前言 本文標(biāo)題為實(shí)戰(zhàn),那么希望你已經(jīng)搭建好了環(huán)境莲绰。如果沒有欺旧,請參考官方文檔進(jìn)行環(huán)境搭建: 官方教程 通過學(xué)習(xí)這個(gè)例...
    CSU_IceLee閱讀 5,155評論 6 12
  • 我是一名九零后少年,同許多人一樣蛤签,熱愛互聯(lián)網(wǎng)工作辞友。在深圳的一家生產(chǎn)汽車車載充電器的企業(yè)工作。來深圳已有一個(gè)月了震肮,感...
    愛生活更愛馬屁豬閱讀 221評論 0 0