serializers是什麼?官網是這樣的"Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into JSON, XML or other content types. "翻譯出來就是,將複雜的數據結構變成json或者xml這個格式的。
在最近的學習中,我的見解,serializers有如下幾個做用:
- 將queryset與model實例等進行序列化,轉化成json格式,返回給用戶(api接口)。
- 將post與patch/put的上來的數據進行驗證。
- 對post與patch/put數據進行處理。html
(後面的內容,將用patch表示put/patch更新,博主認爲patch更貼近更新的說法)
簡單來講,針對get來講,serializers的做用體如今第一條,但若是是其餘請求,serializers可以發揮2,3條的做用!python
本文比較長,用張圖簡單介紹一下主要內容正則表達式
咱們知道在django中,form也有許多field,那serializers其實也是drf中發揮着這樣的功能。咱們先簡單瞭解經常使用的幾個field。數據庫
CharField、BooleanField、IntegerField、DateTimeField這幾個用得比較多,咱們把外鍵的field放到後面去說!django
# 舉例子
mobile = serializers.CharField(max_length=11, min_length=11)
age = serializers.IntegerField(min_value=1, max_value=100)
# format能夠設置時間的格式,下面例子會輸出如:2018-1-24 12:10
pay_time = serializers.DateTimeField(read_only=True,format='%Y-%m-%d %H:%M')
is_hot = serializers.BooleanField()
複製代碼
不一樣的是,咱們在django中,form更強調對提交的表單進行一種驗證,而serializer的field不只在進行數據驗證時起着相當重要的做用,在將數據進行序列化後返回也發揮着重要做用!!
咱們能夠看出,不一樣的field能夠用不一樣的關鍵參數,除此以外,還有一些十分重要有用的參數。編程
read_only:True表示不容許用戶本身上傳,只能用於api的輸出。若是某個字段設置了read_only=True,那麼就不須要進行數據驗證,只會在返回時,將這個字段序列化後返回 舉個簡單的例子:在用戶進行購物的時候,用戶post訂單時,確定會產生一個訂單號,而這個訂單號應該由後臺邏輯完成,而不該該由用戶post過來,若是不設置read_only=True,那麼驗證的時候就會報錯。json
order_sn = serializers.CharField(readonly=True)
複製代碼
write_only: 與read_only對應 required: 顧名思義,就是這個字段是否必填。
allow_null/allow_blank:是否容許爲NULL/空 。 error_messages:出錯時,信息提示。api
name = serializers.CharField(required=True, min_length=6,
error_messages={
'min_length': '名字不能小於6個字符',
'required': '請填寫名字'})
複製代碼
label: 字段顯示設置,如 label='驗證碼' help_text: 在指定字段增長一些提示文字,這兩個字段做用於api頁面比較有用 style: 說明字段的類型,這樣看可能比較抽象,看下面例子:bash
# 在api頁面,輸入密碼就會以*顯示
password = serializers.CharField(
style={'input_type': 'password'})
# 會顯示選項框
color_channel = serializers.ChoiceField(
choices=['red', 'green', 'blue'],
style={'base_template': 'radio.html'})
複製代碼
這裏面,還有一個十分有用的validators參數,這個咱們會在後面說起!數據結構
HiddenField的值不依靠輸入,而須要設置默認的值,不須要用戶本身post數據過來,也不會顯式返回給用戶,最經常使用的就是user!!
咱們在登陸狀況下,進行一些操做,假設一個用戶去收藏了某一門課,那麼後臺應該自動識別這個用戶,而後用戶只須要將課程的id post過來,那麼這樣的功能,咱們配合CurrentUserDefault()實現。
# 這樣就能夠直接獲取到當前用戶
user = serializers.HiddenField(
default=serializers.CurrentUserDefault())
複製代碼
這個標題是官方文檔的一個小標題,我以爲用的很好,一眼看出,這是爲post和patch所設置的,沒錯,這一部分功能是專門爲這兩種請求所設計的,若是隻是簡單的get請求,那麼在設置了前面的field可能就可以知足這個需求。
咱們在view以及mixins的博客中說起到,post請求對應create方法,而patch請求對應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):
# 略去一些稍微無關的內容
···
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的操做,它去調用了serializer的create或update方法,不是mixins中的!!!咱們看一下流程圖(以post爲例)
# 假設如今是個博客,有一個建立文章,與修改文章的功能, model爲Article。
class ArticleSerializer(serializers.Serializer):
user = serializers.HiddenField(
default=serializers.CurrentUserDefault())
name = serializers.CharField(max_length=20)
content = serializers.CharField()
def create(self, validated_data):
# 除了用戶,其餘數據能夠從validated_data這個字典中獲取
# 注意,users在這裏是放在上下文中的request,而不是直接的request
user = self.context['request'].user
name = validated_data['name ']
content = validated_data['content ']
return Article.objects.create(**validated_data)
def update(self, instance, validated_data):
# 更新的特別之處在於你已經獲取到了這個對象instance
instance.name = validated_data.get('name')
instance.content = validated_data.get('content')
instance.save()
return instance
複製代碼
可能會有人好奇,系統是怎麼知道,咱們須要調用serializer的create方法,仍是update方法,咱們從save( )方法能夠看出,判斷的依據是:
if self.instance is not None:pass
複製代碼
那麼咱們的mixins的create與update也已經在爲開發者設置好了,
# CreateModelMixin
serializer = self.get_serializer(data=request.data)
# UpdateModelMixin
serializer = self.get_serializer(instance, data=request.data, partial=partial)
複製代碼
也就是說,在update經過get_object( )的方法獲取到了instance,而後傳遞給serializer,serializer再根據是否有傳遞instance來判斷來調用哪一個方法!
咱們在上面提到field,它能起到必定的驗證做用,但很明顯,它存在很大的侷限性,舉個簡單的例子,咱們要判斷咱們手機號碼,若是使用CharField(max_length=11, min_length=11),它只能確保咱們輸入的是11個字符,那麼咱們須要自定義!
mobile_phone = serializers.CharField(max_length=11, min_length=11)
def validate_mobile_phone(self, mobile_phone):
# 注意參數,self以及字段名
# 注意函數名寫法,validate_ + 字段名字
if not re.match(REGEX_MOBILE, mobile):
# REGEX_MOBILE表示手機的正則表達式
raise serializers.ValidationError("手機號碼非法")
return mobile_phone
複製代碼
固然,這裏面還能夠加入不少邏輯,例如,還能夠判斷手機是否本來就存在數據庫等等。
上面驗證方式,只能驗證一個字段,若是是兩個字段聯合在一塊兒進行驗證,那麼咱們就能夠重載validate( )方法。
start = serializers.DateTimeField()
finish = serializers.DateTimeField()
def validate(self, attrs):
# 傳進來什麼參數,就返回什麼參數,通常狀況下用attrs
if data['start'] > data['finish']:
raise serializers.ValidationError("finish must occur after start")
return attrs
複製代碼
這個方法很是的有用,咱們還能夠再這裏對一些read_only的字段進行操做,咱們在read_only說起到一個例子,訂單號的生成,咱們能夠在這步生成一個訂單號,而後添加到attrs這個字典中。
order_sn = serializers.CharField(readonly=True)
def validate(self, attrs):
# 調用一個方法生成order_sn
attrs['order_sn'] = generate_order_sn()
return attrs
複製代碼
這個方法運用在modelserializer中,能夠剔除掉write_only的字段,這個字段只驗證,但不存在與指定的model當中,即不能save( ),能夠在這delete掉!
validators能夠直接做用於某個字段,這個時候,它與單獨的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])
複製代碼
固然,drf提供的validators還有很好的功能:UniqueValidator,UniqueTogetherValidator等 UniqueValidator: 指定某一個對象是惟一的,如,用戶名只能存在惟一:
username = serializers.CharField(
max_length=11,
min_length=11,
validators=[UniqueValidator(queryset=UserProfile.objects.all())
)
複製代碼
UniqueTogetherValidator: 聯合惟一,如用戶收藏某個課程,這個時候就不能單獨做用於某個字段,咱們在Meta中設置。
class Meta:
validators = [
UniqueTogetherValidator(
queryset=UserFav.objects.all(),
fields=('user', 'course'),
message='已經收藏'
)]
複製代碼
講了不少Serializer的,在這個時候,我仍是強烈建議使用ModelSerializer,由於在大多數狀況下,咱們都是基於model字段去開發。
ModelSerializer已經重載了create與update方法,它可以知足將post或patch上來的數據進行進行直接地建立與更新,除非有額外需求,那麼就能夠重載create與update方法。 ModelSerializer在Meta中設置fields字段,系統會自動進行映射,省去每一個字段再寫一個field。
class UserDetailSerializer(serializers.ModelSerializer):
""" 用戶詳情序列化 """
class Meta:
model = User
fields = ("name", "gender", "birthday", "email", "mobile")
# fields = '__all__': 表示全部字段
# exclude = ('add_time',): 除去指定的某些字段
# 這三種方式,存在一個便可
複製代碼
1,某個字段不屬於指定model,它是write_only,須要用戶傳進來,但咱們不能對它進行save( ),由於ModelSerializer是基於Model,這個字段在Model中沒有對應,這個時候,咱們須要重載validate!
如在用戶註冊時,咱們須要填寫驗證碼,這個驗證碼只須要驗證,不須要保存到用戶這個Model中:
def validate(self, attrs):
del attrs["code"]
return attrs
複製代碼
2,某個字段不屬於指定model,它是read_only,只須要將它序列化傳遞給用戶,可是在這個model中,沒有這個字段!咱們須要用到SerializerMethodField。
假設須要返回用戶加入這個網站多久了,不可能維持這樣加入的天數這樣一個數據,通常會記錄用戶加入的時間點,而後當用戶獲取這個數據,咱們再計算返回給它。
class UserSerializer(serializers.ModelSerializer):
days_since_joined = serializers.SerializerMethodField()
# 方法寫法:get_ + 字段
def get_days_since_joined(self, obj):
# obj指這個model的對象
return (now() - obj.date_joined).days
class Meta:
model = User
複製代碼
固然,這個的SerializerMethodField用法還相對簡單一點,後面還會有比較複雜的狀況。
講了那麼多,終於要研究一下外鍵啦~
其實,外鍵的field也比較簡單,若是咱們直接使用serializers.Serializer,那麼直接用PrimaryKeyRelatedField就解決了。 假設如今有一門課python入門教學(course),它的類別是python(catogory)。
# 指定queryset
category = serializers.PrimaryKeyRelatedField(queryset=CourseCategory.objects.all(), required=True)
複製代碼
ModelSerializer就更簡單了,直接經過映射就行了
不過這樣只是用戶得到的只是一個外鍵類別的id,並不能獲取到詳細的信息,若是想要獲取到具體信息,那須要嵌套serializer
category = CourseCategorySerializer()
複製代碼
注意:上面兩種方式,外鍵都是正向取得,下面介紹怎麼反向去取,如,咱們須要獲取python這個類別下,有什麼課程。 首先,在課程course的model中,須要在外鍵中設置related_name
class Course(model.Model):
category = models.ForeignKey(CourseCategory, related_name='courses')
複製代碼
# 反向取課程,經過related_name
# 一對多,一個類別下有多個課程,必定要設定many=True
courses = CourseSerializer(many=True)
複製代碼
寫到這裏,咱們的外鍵就基本講完了!還有一個小問題:咱們在上面提到ModelSerializer須要解決的第二個問題中,其實還有一種狀況,就是某個字段屬於指定model,但不能獲取到相關數據。
假設如今是一個多級分類的課程,例如,編程語言-->python-->python入門學習課程,編程語言與python屬於類別,另一個屬於課程,編程語言類別是python類別的一個外鍵,並且屬於同一個model,實現方法:
parent_category = models.ForeignKey('self', null=True, blank=True,
verbose_name='父類目別',
related_name='sub_cat')
複製代碼
如今獲取編程語言下的課程,顯然沒法直接獲取到python入門學習這個課程,由於它們兩沒有外鍵關係。SerializerMethodField( )也能夠解決這個問題,只要在自定義的方法中實現相關的邏輯便可!
courses = SerializerMethodField()
def get_courses(self, obj):
all_courses = Course.objects.filter(category__parent_category_id=obj.id)
courses_serializer = CourseSerializer(all_course, many=True,
context={'request': self.context['request']})
return courses_serializer.data
複製代碼
上面的例子看起來有點奇怪,由於咱們在SerializerMethodField()嵌套了serializer,就須要本身進行序列化,而後再從data就能夠取出json數據。
能夠看到傳遞的參數是分別是:queryset,many=True多個對象,context上下文。這個context十分關鍵,若是不將request傳遞給它,在序列化的時候,圖片與文件這些Field不會再前面加上域名,也就是說,只會有/media/img...這樣的路徑!
寫到這,serializers的小結就寫完啦,若是錯誤的地方,歡迎各位的指出!接下來會繼續寫drf的相關文章~~
CSDN: http://blog.csdn.net/l_vip/article/details/79156113