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為例)
如果你的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è)問題
- 某個(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中:
- 某個(gè)字段不屬于指定model禀忆,它是write_only臊旭,需要用戶傳進(jìn)來,但我們不能對它進(jìn)行save( )箩退,因?yàn)镸odelSerializer是基于Model离熏,這個(gè)字段在Model中沒有對應(yīng),這個(gè)時(shí)候戴涝,我們需要重載validate滋戳!
def validate(self, attrs):
del attrs["code"]
return attrs
- 某個(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ì)算返回給它荸镊。
- 某個(gè)字段不屬于指定model,它是read_only可帽,只需要將它序列化傳遞給用戶娄涩,但是在這個(gè)model中,沒有這個(gè)字段映跟!我們需要用到SerializerMethodField蓄拣。
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)