REST
框架中的序列化器的工作方式與Django
Form
和ModelForm
類非常相似之宿。
REST
提供了一個Serializer
類杰扫,可以將對象
中的數(shù)據(jù)序列化成json
,同時還可以將json
數(shù)據(jù)反序列化成對象
第零篇、rest_framework
的配置
# 1.創(chuàng)建一個 `django`項(xiàng)目屯阀,并生成一個`app`,命名為`my_app`
# 在 ALLOWED_HOSTS 允許所有
ALLOWED_HOSTS = ['*']
# 2. 安裝 djangorestframework
# 我這里安裝的 `restframework` 是 `3.11`版本,后續(xù)的用法以及源碼都基于此版本
pip install djangorestframework
# 3.將 restframework 配置到 `django`的`app`中
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'my_app.apps.MyAppConfig',
'rest_framework',
]
# 4.數(shù)據(jù)初始化
python manage.py migrate
第一篇踱葛、rest_framework
的序列化
與反序列化
流程與用法
這里以簡單的數(shù)據(jù)模型來講解土陪,只是為了弄明白 rest_framework
的源碼流程,所以不涉及復(fù)雜關(guān)系笔喉,在第二篇中單獨(dú)講解 關(guān)系模型
1. REST序列化
注意:在 rest_framework
中序列化器不僅適用于django
的model
數(shù)據(jù)而是對所有的對象都適用取视,在這里我們只以django
的model
作為數(shù)據(jù)對象。
使用 rest_framework
的序列化的流程
-
1.創(chuàng)建指定模型的序列化器(繼承
serializers.Serializer
)- 指明需要序列化的字段信息()
id = serializers.IntegerField() name = serializers.CharField() size = serializers.IntegerField()
-
2.在View類中常挚,生成序列化器對象
# instance 就是我們要序列化的對象作谭,也可以傳入query_set book = Book.objects.first() book_serializer = BookSerializer(instance=book) # 如果傳入的instance是復(fù)數(shù),則需要 many=True books = Book.objects.all() books_serializer = BookSerializer(instance=books, many=True)
-
3.從序列化器對象中取出序列化的數(shù)據(jù)
data = book_serializer.data
example1. 簡單的數(shù)據(jù)序列化使用
# models.py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name='書名')
size = models.IntegerField(verbose_name='字?jǐn)?shù)')
# views.py
# 這里使用了 `rest_framework` 的 `Response`,也可以使用 django 提供的JsonResponse
# from django.http import JsonResponse
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import serializers
from my_app.models import Book
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
size = serializers.IntegerField()
class BookView(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk', None) # 從 url 路徑中獲取 pk 字段信息
book = Book.objects.filter(pk=pk).first()
book_serializer = BookSerializer(instance=book)
data = book_serializer.data
return Response(data)
# urls.py
from django.contrib import admin
from django.urls import path
from my_app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('api/book/<int:pk>/', views.BookView.as_view()),
]
使用postman,訪問http://127.0.0.1:8000/api/book/1/
的get
方法
返回的結(jié)果如下:
{
"id": 1,
"name": "斗破蒼穹",
"size": 3450000
}
example 2. 使用source
更改字段名稱
source
指定實(shí)際的數(shù)據(jù)獲取來源(字段
或者函數(shù)
)
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField(source='name') # 綁定 Book 中的 name 字段
size = serializers.IntegerField()
指明title
的數(shù)據(jù)來源
與name
字段
返回結(jié)果如下
{
"id": 1,
"title": "斗破蒼穹", // 這里 name 改成了 title
"size": 3450000
}
example 3. 使用source
獲取函數(shù)返回值
我們有時不僅需要 model
中字段數(shù)據(jù)
奄毡,也需要函數(shù)數(shù)據(jù)
折欠,借助source
可以將函數(shù)
進(jìn)行序列化
# models.py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name='書名')
size = models.IntegerField(verbose_name='字?jǐn)?shù)')
category_choices = (
(1, '仙俠修真'),
(2, '歷史'),
(3, '都市言情'),
)
# `django` 會自動生成`get_category_display()函數(shù)`
category = models.IntegerField(choices=category_choices, default=1)
def big_data(self):
"""是否大于300萬字"""
return self.size > 3000000
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
size = serializers.IntegerField()
category = serializers.IntegerField()
category_display = serializers.CharField(source='get_category_display')
is_big_data = serializers.BooleanField(source='big_data')
序列化后的返回內(nèi)容:
{
"id": 1,
"name": "斗破蒼穹",
"size": 3450000,
"category": 1,
"category_display": "仙俠修真",
"is_big_data": true
}
2. REST反序列化簡單使用
反序列化就是將json
數(shù)據(jù)轉(zhuǎn)成數(shù)據(jù)對象
反序列化使用流程:
- 1.創(chuàng)建序列化器
- 指定序列器的字段以及字段約束
- 重寫
update()
與create()
方法
-
- 生成序列化器對象
- 傳入
data
參數(shù),instance
參數(shù)可選,如果不傳入锐秦,則生成新的對象咪奖,如果傳入instance
參數(shù),則在傳入對象基進(jìn)行更新
book_serializer = BookSerializer(instance=book, data=data)
3.通過
is_valid()
對傳入的數(shù)據(jù)進(jìn)行驗(yàn)證4.執(zhí)行
save()
方法將數(shù)據(jù)反序列化成對象
example 1. 數(shù)據(jù)反序列化(創(chuàng)建對象)
# models.py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name='書名')
size = models.IntegerField(verbose_name='字?jǐn)?shù)')
category_choices = (
(1, '仙俠修真'),
(2, '歷史'),
(3, '都市言情'),
)
category = models.IntegerField(choices=category_choices, default=1)
# views.py
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import serializers
from my_app.models import Book
class BookSerializer(serializers.Serializer):
# 必須添加 read_only=True酱床,否則無法進(jìn)行`create`與`update`操作
id = serializers.IntegerField(read_only=True)
name = serializers.CharField()
size = serializers.IntegerField()
category = serializers.IntegerField()
def create(self, validated_data):
"""
返回一個 instance 實(shí)例對象
"""
name = validated_data.get('name', None)
size = validated_data.get('size', 0)
category = validated_data.get('category', 2)
book = Book.objects.create(
name=name, size=size, category=category
)
book.save() # 更新到數(shù)據(jù)庫中
return book
class BookView(APIView):
def post(self, request, *args, **kwargs):
data = request.data # 從 request 獲取POST傳過來的值
# 沒有傳入 instance 參數(shù)赡艰,在 save() 中執(zhí)行 create(),
book_serializer = BookSerializer(data=data)
if book_serializer.is_valid(): # 驗(yàn)證數(shù)據(jù)是否合法
# 將 json 數(shù)據(jù)轉(zhuǎn)化成 對象斤葱,實(shí)際調(diào)用我們重寫的 `create()`方法
instance = book_serializer.save()
return Response(book_serializer.data)
return Response(book_serializer.errors)
使用postman
發(fā)送post
請求慷垮,如下數(shù)據(jù):
{
"name":"凡人修仙傳",
"size":"4256732",
"category":"1"
}
返回結(jié)果如下:
{
"id": 4,
"name": "凡人修仙傳",
"size": 4256732,
"category": 1
}
可以看出,我們創(chuàng)建了新的 book
對象揍堕,在數(shù)據(jù)庫中的 id
為 4
example 2. 數(shù)據(jù)反序列化(更新對象)
前端發(fā)送json
數(shù)據(jù)料身,在數(shù)據(jù)庫中進(jìn)行更新。
全部更新對應(yīng) put
請求衩茸,部分更新對應(yīng)patch
請求芹血,這里我們以put
請求為例。
對對象進(jìn)行更新楞慈,需要實(shí)現(xiàn)序列化器的update()
方法幔烛,并且在創(chuàng)建序列化器時,傳入instance
實(shí)例對象囊蓝。
# views.py
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import serializers
from my_app.models import Book
class BookSerializer(serializers.Serializer):
# 必須添加 read_only=True饿悬,否則無法進(jìn)行`create`與`update`操作
id = serializers.IntegerField(read_only=True)
name = serializers.CharField()
size = serializers.IntegerField()
category = serializers.IntegerField()
def create(self, validated_data):
"""
返回一個 instance 實(shí)例對象
"""
name = validated_data.get('name', None)
size = validated_data.get('size', 0)
category = validated_data.get('category', 2)
book = Book.objects.create(
name=name, size=size, category=category
)
book.save() # 更新到數(shù)據(jù)庫中
return book
def update(self, instance, validated_data):
"""
對 instance 實(shí)例對象進(jìn)行數(shù)據(jù)更新
"""
for name, value in validated_data.items():
setattr(instance, name, value)
instance.save() # 將數(shù)據(jù)更新到數(shù)據(jù)庫中
return instance
class BookView(APIView):
def post(self, request, *args, **kwargs):
"""用于創(chuàng)建對象"""
data = request.data # 從 request 獲取POST傳過來的值
# 沒有傳入 instance 參數(shù),在 save() 中執(zhí)行 create()聚霜,
book_serializer = BookSerializer(data=data)
if book_serializer.is_valid():
instance = book_serializer.save()
return Response(book_serializer.data)
return Response(book_serializer.errors)
def put(self, request, *args, **kwargs):
"""用于更新資源的部分信息"""
data = request.data # 從 request 獲取傳過來的值
pk = kwargs.get('pk', None) # 從 `url` 路徑中獲取`pk`值
book = Book.objects.get(pk=pk)
book_serializer = BookSerializer(instance=book, data=data)
if book_serializer.is_valid():
instance = book_serializer.save() # 更新后的實(shí)例對象 instance
return Response(book_serializer.data) # 將更新后的實(shí)例進(jìn)行序列化返回的 json 數(shù)據(jù)
return Response(book_serializer.errors)
# urls.py
from django.contrib import admin
from django.urls import path
from my_app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('api/book/<int:pk>/', views.BookView.as_view()),
]
我們對 id
為 4
的book進(jìn)行更新狡恬,使用postman
對http://127.0.0.1:8000/api/book/4/
,發(fā)送put
請求蝎宇,內(nèi)容如下:
{
"name":"凡人修仙傳之都市篇",
"size":"4256732",
"category":3
}
由于我們在 BookSerializer
中定義的id
字段具有read_only=True
約束弟劲,所有我們在發(fā)送的json
數(shù)據(jù)中可以不填id
信息,但是其它的信息必須填寫姥芥。
返回內(nèi)容:
{
"id": 4,
"name": "凡人修仙傳之都市篇",
"size": 4256732,
"category": 3
}
總結(jié):對反序列化而言兔乞,我們要做的就是實(shí)現(xiàn)序列化器的update()
與create()
方法,然后調(diào)用save()
,save()
內(nèi)部會幫我們分發(fā)到update()
或者create()
save()
源碼流程:
查看源碼的技巧:對斷言assert
部分不用太關(guān)注凉唐,抓住主要的邏輯
def save(self, **kwargs):
# 1.傳入自己補(bǔ)充的信息庸追,將補(bǔ)充的信息放入 `validated_data` 中
validated_data = dict(
list(self.validated_data.items()) +
list(kwargs.items())
)
# 如果沒有傳入 `instance`,則調(diào)用 `update`方法
if self.instance is not None:
self.instance = self.update(self.instance, validated_data)
# 否則調(diào)用 `create` 方法
else:
self.instance = self.create(validated_data)
return self.instance
3. REST反序列化熊榛,字段約束
反序列化數(shù)據(jù)時锚国,使用is_valid()
對傳入的data
數(shù)據(jù)進(jìn)行驗(yàn)證。
字段約束
類似于 django
的約束條件
如果發(fā)生任何驗(yàn)證錯誤玄坦,則.errors
屬性將包含代表所得錯誤消息
example 1. 字段缺少展示
如果上述的update
傳入時,size
信息并沒有改動血筑,我們不傳入size
{
"name":"凡人修仙傳之都市篇",
"category":3
}
則返回信息:
{
"size": [
"This field is required."
]
}
這是因?yàn)槲覀兌x的序列化器,如果沒有指定required
為False
,則必須傳入值绘沉,或者指定默認(rèn)值。
改成如下豺总,則不再報錯车伞,可以在更新或者生成時,不傳入size
屬性
class BookSerializer(serializers.Serializer):
# 必須添加 read_only=True喻喳,否則無法進(jìn)行`create`與`update`操作
id = serializers.IntegerField(read_only=True)
name = serializers.CharField()
size = serializers.IntegerField(required=False)
category = serializers.IntegerField()
3.1 所有的字段共有的約束條件以及默認(rèn)值:read_only=False
,write_only=False
,required=None
,allow_null=False
,validators=None
-
read_only
表示是否只讀屬性另玖,一般用于id
,表示只能看表伦,不能改
谦去,就算我們傳入了id
信息,如:
返回的結(jié)果中蹦哼,{ "id":5, "name":"凡人修仙傳之都市篇", "category":3 }
id
依然不會改變鳄哭,這是因?yàn)樵?code>create和update
中,read_only=True
的信息根本不會被封裝到validated_data
中
如果我們對某字段使用了read_only=True
纲熏,還想要在 create
和update
中對數(shù)據(jù)進(jìn)行修改妆丘,可以在 save()
方法中,將信息傳入
pk = kwargs.get('pk',None) # 從 url 中獲取`pk`信息
book_serializer.save(id=pk)
備注:這里只是為了演示save()
從外部獲取數(shù)據(jù)局劲,但是不建議更改id
字段
-
write_only
表示只寫屬性勺拣,一般用于一些保密的信息,例如password
鱼填,只能用于創(chuàng)建或更新药有,但是返回時,不對該字段信息進(jìn)行序列化剔氏。
例如我們對上述的name
字段添加write_only
約束塑猖,則更新后返回的信息中不再具有name
{
"id": 4,
"size": 4256732,
"category": 3
}
-
required
表示字段是否必填竹祷,如果required = False
谈跛,則可以不傳入該字段信息。 -
allow_null
表示傳入的數(shù)據(jù)是否可以為空塑陵。 -
validators
(驗(yàn)證器列表)感憾,可以傳入多個函數(shù)對象(callback
),通過函數(shù)對字段進(jìn)行自定義約束令花。
example 2. 使用字段的validators
阻桅,實(shí)現(xiàn)自定義約束
實(shí)現(xiàn)自定義約束函數(shù),返回結(jié)果只能有兩種
- 返回一個驗(yàn)證后的數(shù)據(jù) 兼都,表示驗(yàn)證通過
- 拋出
ValidationError
異常嫂沉,表示驗(yàn)證不通過
from rest_framework.exceptions import ValidationError
def sensitive_word_filter(value):
"""
敏感字過濾
返回驗(yàn)證通過的數(shù)據(jù),不通過則拋出 `ValidationError` 異常
拋出 `ValidationError` 由 `is_valid()`接收
"""
sensitive_words = ['*****', '***', '****']
if value in sensitive_words:
raise ValidationError(f'{value}是敏感詞')
return value
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
# 注意這里傳入的函數(shù)對象扮碧,而不是 sensitive_word_filter()
name = serializers.CharField(validators=[sensitive_word_filter, ])
size = serializers.IntegerField(required=False)
category = serializers.IntegerField()
3.2 特殊字段的獨(dú)有約束條件
和django
一樣趟章,除了共有的約束信息杏糙,rest_framework
中不同的字段,有不同的約束條件
-
CharField
:min_length
,max_length
,trim_whitespace
,allow_blank
-
EmailField
: 自動驗(yàn)證是否符合郵箱
格式 -
IntegerField
,FloatField
:max_value
,min_value
-
DateTimeField
:format
序列化時的輸出格式 蚓土,input_formats
反序列化的輸入字符串格式列表宏侍,可以使用多個格式;default_timezone
,設(shè)置時區(qū)
example 3. 使用DateTimeField
的獨(dú)有約束蜀漆,實(shí)現(xiàn)自定義時間的輸入
,輸出
格式
# models.py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name='書名')
size = models.IntegerField(verbose_name='字?jǐn)?shù)')
category_choices = (
(1, '仙俠修真'),
(2, '歷史'),
(3, '都市言情'),
)
category = models.IntegerField(choices=category_choices, default=1)
# 這里 created 是后續(xù)加上去的谅河,必須要有 null=True, blank=True 才能遷移成功
created = models.DateTimeField(null=True, blank=True)
# views.py
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import serializers
from my_app.models import Book
from my_app.utils.validators import sensitive_word_filter
format = '%Y-%m-%d %H:%M:%S'
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
# 注意這里傳入的函數(shù)對象,而不是 sensitive_word_filter()
name = serializers.CharField(validators=[sensitive_word_filter, ])
size = serializers.IntegerField(required=False)
category = serializers.IntegerField()
# 指定輸入與輸出的字符串格式
created = serializers.DateTimeField(format=format, input_formats=[format, ])
def create(self, validated_data):
"""
返回一個 instance 實(shí)例對象
"""
name = validated_data.get('name', None)
size = validated_data.get('size', 0)
category = validated_data.get('category', 2)
book = Book.objects.create(
name=name, size=size, category=category
)
book.save() # 更新到數(shù)據(jù)庫中
return book
def update(self, instance, validated_data):
for name, value in validated_data.items():
setattr(instance, name, value)
instance.save()
return instance
class BookView(APIView):
def get(self, request, *args, **kwargs):
"""獲取指定 pk 的 `book` 信息"""
pk = kwargs.get('pk', None)
book = Book.objects.get(pk=pk)
book_serializer = BookSerializer(instance=book)
return Response(book_serializer.data)
def put(self, request, *args, **kwargs):
"""用于更新資源信息"""
data = request.data # 從 request 獲取傳過來的值
pk = kwargs.get('pk', None)
book = Book.objects.get(pk=pk)
# 傳入了instance 對象确丢,`.save()`執(zhí)行更新操作
book_serializer = BookSerializer(instance=book, data=data)
if book_serializer.is_valid():
instance = book_serializer.save() # 更新后的實(shí)例對象 instance
return Response(book_serializer.data) # 將更新后的實(shí)例進(jìn)行序列化返回的 json 數(shù)據(jù)
return Response(book_serializer.errors)
# urls.py
from django.contrib import admin
from django.urls import path
from my_app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('api/book/<int:pk>/', views.BookView.as_view()),
]
對 http://127.0.0.1:8000/api/book/3/
發(fā)送put
請求绷耍,請求內(nèi)容:
{
"name":"雪中悍刀行",
"category":3,
"created":"2013-11-04 05:23:49"
}
返回內(nèi)容:
{
"id": 3,
"name": "雪中悍刀行",
"size": 4651209,
"category": 3,
"created": "2013-11-04 05:23:49"
}
如果請求輸入的datetime 格式可能有多種,我們也可以在input_formats
設(shè)置多個
3.3 自定義驗(yàn)證函數(shù)
在 3.1 中使用在字段上validators
可以實(shí)現(xiàn)自定義函數(shù)驗(yàn)證鲜侥,除了這一種方法外锨天,我們還可以使用另一種驗(yàn)證方式。
創(chuàng)建.validate_<field_name>
方法可以指定自定義字段級驗(yàn)證
如下剃毒,為username
綁定自定義函數(shù)約束/驗(yàn)證
example 4. .validate_<field_name>
函數(shù)級驗(yàn)證
# models.py
from django.db import models
class UserInfo(models.Model):
username = models.CharField(max_length=16)
password = models.CharField(max_length=64)
email = models.CharField(max_length=64)
created_date = models.DateField()
money = models.IntegerField()
from rest_framework.views import APIView
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from my_app.models import UserInfo
format = '%Y-%m-%d'
input_formats = ['%Y-%m-%d', '%Y %m %d', '%Y/%m/%d', '%Y.%m.%d']
class UserInfoSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
username = serializers.CharField(max_length=16)
password = serializers.CharField(max_length=64, write_only=True)
email = serializers.EmailField(max_length=64)
created_date = serializers.DateField(format=format, input_formats=input_formats)
money = serializers.FloatField(min_value=0)
def validate_username(self, value):
if not value.startswith('中國公民_'):
raise ValidationError('用戶名稱必須以`中國公民_`開頭')
return value
def create(self, validated_data):
user = UserInfo()
for key, value in validated_data.items():
setattr(user, key, value)
user.save()
return user
class UserView(APIView):
def post(self, request, *args, **kwargs):
data = request.data
user_serializer = UserInfoSerializer(data=data)
if user_serializer.is_valid():
user_serializer.save()
return Response(user_serializer.data)
return Response(user_serializer.errors)
from django.contrib import admin
from django.urls import path
from my_app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('api/book/<int:pk>/', views.BookView.as_view()),
path('api/user/<int:pk>/', views.UserView.as_view()),
]
發(fā)送post
請求病袄,創(chuàng)建時,username
必須以中國公民_
開頭'
validate_<field_name>
函數(shù)約束與validators
約束的區(qū)別:
最主要的是validate_<field_name>
只能給某一個字段使用赘阀,而validators
適用于多個字段益缠。
我們一般把通用型的字段驗(yàn)證放在validators
中,以便供更多的字段適用基公。
3.4 對象級驗(yàn)證
3.1 - 3.3 都是對一個字段
進(jìn)行驗(yàn)證幅慌,但在特殊的情況下,我們可能涉及到多個字段之間的關(guān)系驗(yàn)證轰豆,這就需要我們直接對 data
進(jìn)行驗(yàn)證胰伍。
重寫 Serializer
的validate()
方法,可以直接對data
進(jìn)行處理酸休。
- 拋出異常
ValidationError
說明驗(yàn)證失敗 - 返回處理后的
dict
格式的數(shù)據(jù),說明驗(yàn)證成功
class MySerializer(serializers.Serializer):
def validate(self, data):
if data['start'] > data['end']:
raise ValidationError('開始時間必須小于結(jié)束時間')
return data
4. REST多個對象的序列化
與反序列化
前面的講解中骂租,我們都是使用的單個對象的獲取,但是在實(shí)際工作中斑司,我們更多的是一次獲取多個對象渗饮,并進(jìn)行分頁。
example 1.通過 get
獲取多個對象數(shù)據(jù) 以及通過post
創(chuàng)建多個對象
# models.py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name='書名')
size = models.IntegerField(verbose_name='字?jǐn)?shù)')
category_choices = (
(1, '仙俠修真'),
(2, '歷史'),
(3, '都市言情'),
)
category = models.IntegerField(choices=category_choices, default=1)
created = models.DateTimeField(null=True, blank=True)
# views.py
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import serializers
from my_app.models import Book, UserInfo
from rest_framework.exceptions import ValidationError
format = '%Y-%m-%d %H:%M:%S'
input_formats = ['%Y-%m-%d %H:%M:%S', '%Y %m %d %H:%M:%S', '%Y/%m/%d %H:%M:%S', '%Y.%m.%d %H:%M:%S']
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField()
size = serializers.IntegerField()
category = serializers.IntegerField()
created = serializers.DateTimeField(format=format, input_formats=input_formats)
def create(self, validated_data):
"""
返回一個 instance 實(shí)例對象
"""
book = Book(validated_data)
book.save() # 更新到數(shù)據(jù)庫中
return book
def validate_category(self, value):
"""對類別 category 進(jìn)行約束驗(yàn)證"""
allow_category = [1, 2, 3]
if value not in allow_category:
raise ValidationError('類別不符合')
return value
class BooksView(APIView):
def get(self, request, *args, **kwargs):
books = Book.objects.all()
book_serializers = BookSerializer(instance=books, many=True)
return Response(book_serializers.data)
def post(self, request, *args, **kwargs):
data = request.data
book_serializers = BookSerializer(data=data, many=True) # 這里傳入的是多個對象宿刮,必須使 `many = True`
if book_serializers.is_valid():
book_serializers.save()
return Response(book_serializers.data)
return Response(book_serializers.errors)
# urls.py
from django.contrib import admin
from django.urls import path
from my_app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('api/books/', views.BooksView.as_view()),
]
發(fā)送 get
請求互站,獲取全部的books
數(shù)據(jù)
[
{
"id": 1,
"name": "斗破蒼穹",
"size": 3450000,
"category": 1,
"created": null
},
{
"id": 2,
"name": "大主宰",
"size": 3982365,
"category": 1,
"created": null
},
{
"id": 3,
"name": "雪中悍刀行",
"size": 4651209,
"category": 3,
"created": "2013-11-04"
},
{
"id": 4,
"name": "凡人修仙傳之都市篇",
"size": 4256732,
"category": 3,
"created": null
}
]
發(fā)送 post
請求進(jìn)行創(chuàng)建,如果數(shù)據(jù)中有一個對象的數(shù)據(jù)驗(yàn)證失敗僵缺,則本次創(chuàng)建失效
當(dāng) many = True
時胡桃,我們創(chuàng)建的序列化器的實(shí)例對象并不是我們當(dāng)前的BookSerializer
對象,而是 ListSerializer
對象磕潮,但是我們調(diào)用 ListSerializer
對象的 is_valid()
以及 save()
方法時候翠胰,好像都是BookSerializer
在起作用懊纳,這是因?yàn)?ListSerializer
內(nèi)部將傳入的集合對象進(jìn)行迭代遍歷,然后再交給
BookSerializer
處理
如下是ListSerializer
源碼
# ListSerializer 類
def create(self, validated_data):
# validated_data 是一個集合對象
return [
# 這里的 `child` 就是`BookSerializer`實(shí)例對象
self.child.create(attrs) for attrs in validated_data
]
example 2.自定義ListSerializer
的某些功能
- 第一步 繼承
ListSerializer
亡容,并重寫部分功能
class MyListSerializer(serializers.ListSerializer):
def create(self, validated_data):
# do something
return super().create(validated_data)
- 第二步嗤疯,在使用序列化類時,指定
list_serializer_class
屬性
class BookSerializer(serializers.Serializer):
...
class Meta:
list_serializer_class = BookListSerializer
這樣我們在執(zhí)行 save()
方法時闺兢,會調(diào)用MyListSerializer
的create()
方法
5. REST序列化
與反序列化
的源碼流程
5.1 序列化
源碼流程
查看源碼
的一些技巧
- 1.找到主要的
入口
點(diǎn)茂缚,一步步理出脈絡(luò)
,而不是從import ……·
一行行看起 - 2.對于有繼承關(guān)系的類屋谭,每次執(zhí)行
方法
都從頂層類
找起
class A:
def step_1(self):
print('A.step_1')
self.step_2()
def step_2(self):
print('A.step_2')
self.step_3()
def step_3(self):
print('A.step_3')
class B(A):
def step_2(self):
print('B.step_2')
self.step_3()
class C(B):
def step_3(self):
print('C.step_3')
if __name__ == '__main__':
c = C()
c.step_1()
打印結(jié)果如下:
A.step_1
B.step_2
C.step_3
這里雖然第一步執(zhí)行了A.step_1
,在A.step_1
中執(zhí)行self.step_2()
并不會直接執(zhí)行A.step_2
脚囊,而是按照繼承關(guān)系,先從c
對象查找step_2()
方法桐磁,沒有的話就繼續(xù)往上查找悔耘,找到B
的step_2
,所以先執(zhí)行B.step_2
我擂,每一步都是如此衬以,從頂層查找
,底層的方法只會被覆蓋校摩。
- 3.抓住主要的邏輯 對于
assert
這些驗(yàn)證看峻,或者拋出異常的部分,可以不用太關(guān)注衙吩。
例如完整的save()
方法如下
def save(self, **kwargs):
assert not hasattr(self, 'save_object'), (
'Serializer `%s.%s` has old-style version 2 `.save_object()` '
'that is no longer compatible with REST framework 3. '
'Use the new-style `.create()` and `.update()` methods instead.' %
(self.__class__.__module__, self.__class__.__name__)
)
assert hasattr(self, '_errors'), (
'You must call `.is_valid()` before calling `.save()`.'
)
assert not self.errors, (
'You cannot call `.save()` on a serializer with invalid data.'
)
# Guard against incorrect use of `serializer.save(commit=False)`
assert 'commit' not in kwargs, (
"'commit' is not a valid keyword argument to the 'save()' method. "
"If you need to access data before committing to the database then "
"inspect 'serializer.validated_data' instead. "
"You can also pass additional keyword arguments to 'save()' if you "
"need to set extra attributes on the saved model instance. "
"For example: 'serializer.save(owner=request.user)'.'"
)
assert not hasattr(self, '_data'), (
"You cannot call `.save()` after accessing `serializer.data`."
"If you need to access data before committing to the database then "
"inspect 'serializer.validated_data' instead. "
)
validated_data = dict(
list(self.validated_data.items()) +
list(kwargs.items())
)
if self.instance is not None:
self.instance = self.update(self.instance, validated_data)
assert self.instance is not None, (
'`update()` did not return an object instance.'
)
else:
self.instance = self.create(validated_data)
assert self.instance is not None, (
'`create()` did not return an object instance.'
)
return self.instance
這里面最重要的就只有三段
,我們主要理解這三段就好互妓,后面有時間再研究那些assert
驗(yàn)證
- 4.
見名猜意
,我們根據(jù)方法的名稱先假設(shè)作用
,把整個走一下坤塞,如果能走的通冯勉,說明我們的假設(shè)
應(yīng)該問題不大,然后再去驗(yàn)證我們的假設(shè)對不對摹芙。
備注:這里的源碼分析灼狰,我們也是將方法中的主要邏輯
代碼片段貼出來,沒有全部展示瘫辩。
當(dāng)我們執(zhí)行序列化時伏嗜。通過book_serializer.data
來獲取數(shù)據(jù)
- 第一步: 執(zhí)行
Serializer
的data
,Serializer
的data
調(diào)用父類BaseSerializer
的data
.
在BaseSerializer
的data
中伐厌,主要執(zhí)行以下片段
# 1.具有`instance`實(shí)例對象,則將實(shí)例對象序列化
if self.instance is not None and not getattr(self, '_errors', None):
self._data = self.to_representation(self.instance)
# `_validated_data` 是我們傳入的`data`參數(shù)經(jīng)`is_valid()`驗(yàn)證后的數(shù)據(jù)
# 2.沒有`instance`實(shí)例裸影,但是傳入了`data`數(shù)據(jù)挣轨,并驗(yàn)證通過,將`data`數(shù)據(jù)序列化
elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
self._data = self.to_representation(self.validated_data)
- 第二步:執(zhí)行
to_representation()
函數(shù)
這里Serializer
具有to_representation()
函數(shù)轩猩,所以直接調(diào)用
注意:這里的參數(shù)instance
既可以是數(shù)據(jù)對象
卷扮,也可以是validated_data
字典數(shù)據(jù)
def to_representation(self, instance):
"""
Object instance -> Dict of primitive datatypes.
"""
# 創(chuàng)建有序的 dict
# 這樣返回的`json`數(shù)據(jù)順序就會和我們創(chuàng)建的`Serializer`中聲明的一致
ret = OrderedDict()
# 獲取可讀的字段荡澎,如果字段約束條件中`write_only = True`,則在這里無法獲取晤锹,也就不會進(jìn)行序列化
fields = self._readable_fields
# fields 就是我們聲明的字段 例如:name = serializers.CharField()
for field in fields:
try:
# 如果`instance`中是數(shù)據(jù)對象
# 從我們提供的屬性名稱/方法名稱摩幔,如:`name`,`get_choice_display`
# 通過反射 獲取屬性值
# 如果`instance`是 字典數(shù)據(jù),也就是`validated_data`
# 則直接返回 instance[attr]
attribute = field.get_attribute(instance)
except SkipField:
continue
check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
if check_for_none is None:
ret[field.field_name] = None
else:
# 執(zhí)行各個字段的 `to_representation()`方法
# 主要是將數(shù)據(jù)轉(zhuǎn)化為各個字段的格式
# 例如 `IntegetField` 中,執(zhí)行 int(value)
# 在 `CharField`中鞭铆,執(zhí)行 str(value)
# 在 `DateTimeField`中或衡,則根據(jù)我們傳入的`format`參數(shù),對數(shù)據(jù)進(jìn)行字符串格式化
ret[field.field_name] = field.to_representation(attribute)
return ret
從上面看车遂,rest_framework
的數(shù)據(jù)序列化主要都是在to_representation
中完成封断,如果我們要自定義數(shù)據(jù)序列化的格式,則可以重寫to_representation
from collections import OrderedDict
from rest_framework import serializers
class UpperKeySerializer(serializers.Serializer):
……
def to_representation(self, instance):
"""
重寫了`to_representation`,返回結(jié)果中的`key`,全部返回`大寫`
"""
origin_ret = super().to_representation(instance)
ret = OrderedDict()
for field_name, value in origin_ret.items():
ret[field_name.upper] = value
return ret
這里有一個注意點(diǎn)舶担,我們執(zhí)行post
操作時坡疼,創(chuàng)建序列化器對象時,并沒有傳入instance
衣陶,而是傳入了data = data
柄瑰,并執(zhí)行is_valid()
驗(yàn)證通過,但是序列化
出來的結(jié)果中包含id
屬性剪况,這明顯是直接將instance
序列化了狱意。
這是因?yàn)槲覀儓?zhí)行save()
操作,在save()
中將create()
創(chuàng)建的instance
賦值給了self
.所以self.instance
為真拯欧,to_representation
也就直接作用于該instance
详囤,而不是我們傳入的data = data
.
5.2反序列化
源碼流程
執(zhí)行反序列化
主要有兩步:
book_serailizer.is_valid()
-
book_serailizer.save()
其中save()
方法已經(jīng)講解過,就是調(diào)用create
或者update
方法镐作。
反序列化的源碼主要在數(shù)據(jù)約束 / 驗(yàn)證上比較復(fù)雜藏姐,這里主要討論這一部分。
-
第一步该贾,執(zhí)行
Serializer
類的is_valid()
def is_valid(self, raise_exception=False): # 是否有`_validated_data` 是判斷是否執(zhí)行過`is_valid()`的依據(jù) # 如果存在`_validated_data` 羔杨,說明已經(jīng)驗(yàn)證過,直接返回杨蛋;防止多次調(diào)用`is_valid` if not hasattr(self, '_validated_data'): try: # `is_valid`主要就是執(zhí)行這一行 # `initial_data` 就是我們傳入的`data = data`參數(shù)兜材,在` __init__()`中被初始化為`initial_data` self._validated_data = self.run_validation(self.initial_data) except ValidationError as exc: self._validated_data = {} self._errors = exc.detail else: self._errors = {} if self._errors and raise_exception: raise ValidationError(self.errors) return not bool(self._errors)
從這里可以看出,如果
is_valid()
執(zhí)行成功逞力,則會產(chǎn)生_validated_data
數(shù)據(jù)曙寡,在create
與update
中傳入的validated_data
實(shí)際就是這里的_validated_data
第二步,執(zhí)行
Serializer
類的run_validation()
def run_validation(self, data=empty):
# 1.驗(yàn)證傳入的`data`是否為空
(is_empty_value, data) = self.validate_empty_values(data)
if is_empty_value:
return data
# 2.將數(shù)據(jù)轉(zhuǎn)化為`內(nèi)部值`
# 這里進(jìn)行了第一種驗(yàn)證
# 這里的 `data` 是我們傳入的字典寇荧,那么返回出來的 `value`應(yīng)該也是字典
value = self.to_internal_value(data)
try:
# 這里進(jìn)行了第二種驗(yàn)證举庶,添加驗(yàn)證器
self.run_validators(value)
# 這里進(jìn)行了第三種驗(yàn)證,也就是我們前面`3.4`章節(jié)講的對象級驗(yàn)證揩抡,鉤子函數(shù)就埋在這
value = self.validate(value)
assert value is not None, '.validate() should return the validated data'
except (ValidationError, DjangoValidationError) as exc:
raise ValidationError(detail=as_serializer_error(exc))
return value
上面的第三種驗(yàn)證户侥,在我們的3.4
已經(jīng)講過镀琉,這里不再講解,第二種驗(yàn)證是自定義驗(yàn)證器類
蕊唐,比較復(fù)雜屋摔,一般用不到,這里下一步著重講解to_internal_value
- 第三步替梨,
Serailizer
類實(shí)現(xiàn)了to_internal_value()
钓试,我們直接查看
def to_internal_value(self, data):
"""
這里主要執(zhí)行字段級別的驗(yàn)證
對`data`字典的驗(yàn)證在`run_validators(value)`與 `validate(value)`完成
"""
# 創(chuàng)建一個有序字典
ret = OrderedDict()
errors = OrderedDict()
# 獲取可寫 / 可反序列化的字段對象
# 例如我們將`id`設(shè)置為`read_only = True`,則這里不會獲取`id`字段
fields = self._writable_fields
for field in fields:
# 第一種字段約束
# 這里執(zhí)行自定義函數(shù)驗(yàn)證
# 我們`3.3`章節(jié)`validate_<field_name>`的鉤子埋在這
validate_method = getattr(self, 'validate_' + field.field_name, None)
primitive_value = field.get_value(data)
try:
# 第二種字段約束
# 這里執(zhí)行字段的基礎(chǔ)驗(yàn)證
# 對應(yīng)我們的`3.1`, `3.2`字段約束部分
# 3.1中 `required ` , `validators ` ……
# 3.2中`CharField`的 `max_length`耙替,`IntegerField`的`max_value`等都是在 `field.run_validation`中完成
validated_value = field.run_validation(primitive_value)
if validate_method is not None:
validated_value = validate_method(validated_value)
except ValidationError as exc:
errors[field.field_name] = exc.detail
except DjangoValidationError as exc:
errors[field.field_name] = get_error_detail(exc)
except SkipField:
pass
else:
set_value(ret, field.source_attrs, validated_value)
if errors:
raise ValidationError(errors)
return ret
6. REST ModelSerializer
使用
對于 rest_framework
的序列化器亚侠,所有的基礎(chǔ)功能都是基于Serializer
來實(shí)現(xiàn)的,除此之外rest_framework
還提供了與Django
模型定義緊密映射的序列化程序類ModelSerializer
俗扇。
ModelSerializer
繼承了Serializer
硝烂,它主要有以下三種新的功能:
- 它將根據(jù)模型自動為您生成一組字段,這也是它最主要的作用铜幽。
- 它將自動為序列化器生成驗(yàn)證器滞谢,例如unique_together驗(yàn)證器。
- 它包括簡單的默認(rèn)實(shí)現(xiàn)
create()
和update()
6.1 ModelSerializer
自動生成字段
# models.py
from django.db import models
class UserInfo(models.Model):
username = models.CharField(max_length=16)
password = models.CharField(max_length=32)
money = models.FloatField()
category_choices = (
(1, '普通用戶'),
(2, 'VIP'),
)
category = models.IntegerField(choices=category_choices)
example 1 使用fields
自動生成所有字段
from rest_framework.serializers import ModelSerializer
class UserSerializer(ModelSerializer):
class Meta:
model = UserInfo
fields = '__all__'
使用pycharm
編輯器自帶的Python Console
進(jìn)入shell
界面
[ shell
中自動導(dǎo)入了django
環(huán)境除抛,可以直接使用django
中的model
對象狮杨,否則會報ImproperlyConfigured
錯誤 ]
在 shell
中執(zhí)行以下
>>> from my_app.models import UserSerializer
>>> user_serializer = us = UserSerializer()
>>> user_serializer
打印結(jié)果如下:
UserSerializer():
id = IntegerField(label='ID', read_only=True)
username = CharField(max_length=16)
password = CharField(max_length=32)
money = FloatField()
category = ChoiceField(choices=((1, '普通用戶'), (2, 'VIP')))
可以看出,ModelSerializer
自動為我們創(chuàng)建了字段到忽,并添加了字段約束橄教,像read_only=True
會自定為 django
的主鍵添加,而max_length
也是從UserInfo
中獲取的喘漏。這樣可以為我們節(jié)省很多時間护蝶。
生成的UserSerializer
實(shí)際與我們自己前面所寫的Serializer
功能是一模一樣
的
example 2 自動生成部分字段
class UserSerializer(ModelSerializer):
class Meta:
model = UserInfo
fields = ['id', 'username', 'password'] # `fields` 可以為`__all__` 或者一個列表
example 3 使用exclude
屬性設(shè)置為要從序列化器中排除的字段列表。
class UserSerializer(ModelSerializer):
class Meta:
model = UserInfo
exclude = ['category', 'money']
注意:從3.3.0版開始翩迈,必須提供屬性fields
或exclude
之一持灰。
6.2 使用extra_kwargs
設(shè)置關(guān)鍵字參數(shù)
使用 ModelSerializer
可以方便的幫我們生成字段,并且根據(jù)model
自動添加了約束,如:id
字段的read_only = True
负饲,CharField
的max_length
等堤魁。
但是有的時候我們還是需要自己設(shè)置關(guān)鍵字參數(shù),例如給password
添加write_only = True
,給money
字段添加min_value = 0.0
一種方式是重寫這些字段
class UserSerializer(ModelSerializer):
password = serializers.CharField(write_only=True, max_length=32)
money = serializers.FloatField(min_value=0.0)
class Meta:
model = UserInfo
fields = '__all__'
除了上面的方法返十,也可以通過extra_kwargs
為指定的字段
添加額外的關(guān)鍵字參數(shù)
class UserSerializer(ModelSerializer):
class Meta:
model = UserInfo
fields = '__all__'
extra_kwargs = {
# `ModelSerializer` 生成的'password'已經(jīng)有`max_length = 32`妥泉,這里不需要再設(shè)置。
'password': {'write_only': True},
'money': {'min_value': 0.0}
}
6.3 ModelSerializer
設(shè)置關(guān)系深度depth
當(dāng)我們的model
模型中涉及與其它表的關(guān)系時吧慢,使用ModelSerializer
生成的關(guān)系一般使用主鍵
表示
# models.py
from rest_framework.serializers import ModelSerializer
from django.db import models
class User(models.Model):
username = models.CharField(max_length=16)
detail = models.CharField(max_length=256)
class Article(models.Model):
title = models.CharField(max_length=64)
user = models.ForeignKey(User, on_delete=models.CASCADE)
class ArticleSerializer(ModelSerializer):
class Meta:
model = Article
在 Django
的shell
界面執(zhí)行以下:
>>> from my_app.models import ArticleSerializer
>>> article_ser = ArticleSerializer()
>>> article_ser
打印內(nèi)容如下
ArticleSerializer():
id = IntegerField(label='ID', read_only=True)
title = CharField(max_length=64)
user = PrimaryKeyRelatedField(queryset=User.objects.all())
序列化后的數(shù)據(jù)如下:
[
{
"id": 1,
"title": "梟臣",
"user": 2
},
{
"id": 2,
"title": "斗破蒼穹",
"user": 1
}
]
這里的user
以主鍵代替User
表
當(dāng)設(shè)置depth = 1
后
class ArticleSerializer(ModelSerializer):
class Meta:
model = Article
depth = 1 # depth 是整數(shù)涛漂,表示向下關(guān)系深度,一般不建議在3層以上
[
{
"id": 1,
"title": "梟臣",
"user": {
"id": 2,
"username": "更俗",
"detail": "更俗检诗,原為起點(diǎn)中文網(wǎng)白金作家匈仗,2010年轉(zhuǎn)投縱橫中文網(wǎng)成為其簽約作家。"
}
},
{
"id": 2,
"title": "斗破蒼穹",
"user": {
"id": 1,
"username": "天蠶土豆",
"detail": "天蠶土豆逢慌,本名李虎悠轩。1989年12月28日出生于四川,中國內(nèi)地網(wǎng)絡(luò)小說作家攻泼、85后著名作家火架、浙江省網(wǎng)絡(luò)作家協(xié)會副主席..."
}
}
]
注意:使用depth
后,關(guān)系嵌套為只讀
忙菠。也就是這里的user
為只讀屬性何鸡。
第二篇、rest_framework序列化器關(guān)系
牛欢。
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=64)
create = models.DateTimeField('發(fā)布時間')
visitors = models.IntegerField('閱讀人數(shù)')
user = models.ForeignKey(User, on_delete=models.CASCADE)
在上面的模型中骡男,對于簡單的字段,我們已經(jīng)知道如何序列化傍睹,但是對user
知道關(guān)系
字段隔盛,我們用專門的序列化字段
可選。
-
PrimaryKeyRelatedField
, 可以通過關(guān)系的主鍵來表示關(guān)系對象拾稳。推薦使用 -
StringRelatedField
,使用__str__
方法值來表示關(guān)系對象吮炕。 -
HyperlinkedRelatedField
,通過超鏈接來表示關(guān)系對象。 -
SlugRelatedField
,使用目標(biāo)上的字段來表示關(guān)系對象访得。
7.1 PrimaryKeyRelatedField
使用
from django.db import models
from rest_framework.serializers import ModelSerializer
class User(models.Model):
username = models.CharField(max_length=16)
detail = models.CharField(max_length=256)
class Article(models.Model):
title = models.CharField(max_length=64)
user = models.ForeignKey(User, on_delete=models.CASCADE)
class ArticleSerializer(ModelSerializer):
# 重寫 `user`字段
user = PrimaryKeyRelatedField(queryset=User.objects.filter(pk__in=[1, 2]))
class Meta:
model = Article
fields = '__all__'
序列化后的結(jié)果為:
[
{
"id": 1,
"user": 2,
"title": "楚臣"
},
{
"id": 2,
"user": 2,
"title": "官場之風(fēng)流人生"
},
{
"id": 3,
"user": 2,
"title": "梟臣"
},
{
"id": 4,
"user": 1,
"title": "斗破蒼穹"
},
{
"id": 5,
"user": 1,
"title": "武動乾坤"
}
]
這里我們使用了PrimaryKeyRelatedField
龙亲,所以就是使用user
的主鍵來表示user
對象。
注意:PrimaryKeyRelatedField
等關(guān)系
字段的關(guān)鍵字參數(shù)悍抑。
- 共有的基礎(chǔ)設(shè)置鳄炉, 3.1的
read_only=False
,write_only=False
,required=None
,allow_null=False
,validators=None
。
這里比較常用的就是read_only = True
传趾,將關(guān)系設(shè)置為只讀屬性迎膜。 -
many = False
,如果我們的關(guān)系對象為多個浆兰,則需要設(shè)置many = True
磕仅,這樣序列化的字段會表示為列表
class UserSerializer(ModelSerializer):
# `article_set` 是`User` 的反向關(guān)系字段
# 在 `Django`中,多對一的反向字段都是以 `<fieldname>_set`
# 如果我們不明確指定簸呈,`ModelSerializer`不會自動創(chuàng)建
# 我們在創(chuàng)建 / 更新`User`對象時榕订,不需要`article`參數(shù),所以`read_only = True`
article_set = PrimaryKeyRelatedField(read_only = True,many = True)
class Meta:
model = User
fields = '__all__'
[
{
"id": 1,
"article_set": [
5,
6
],
"username": "天蠶土豆",
"detail": "天蠶土豆蜕便,本名李虎劫恒。1989年12月28日出生于四川,中國內(nèi)地網(wǎng)絡(luò)小說作家、85后著名作家两嘴、浙江省網(wǎng)絡(luò)作家協(xié)會副主席..."
},
{
"id": 2,
"article_set": [
2,
3,
4
],
"username": "更俗",
"detail": "更俗丛楚,原為起點(diǎn)中文網(wǎng)白金作家,2010年轉(zhuǎn)投縱橫中文網(wǎng)成為其簽約作家憔辫。"
}
]
-
queryset
趣些,用于創(chuàng)建
或者更新
的字段約束,例如ArticleSerializer
中贰您,除了常規(guī)的字段約束驗(yàn)證坏平,user
必須在queryset
中。
例如以下數(shù)據(jù)就不通過:
{
"user": 3,
"title": "楚臣2"
}
queryset
與read_only = True
必須二選一锦亦。
7.2 StringRelatedField
使用
class User(models.Model):
username = models.CharField(max_length=16)
detail = models.CharField(max_length=256)
def __str__(self):
return self.username
class Article(models.Model):
title = models.CharField(max_length=64)
user = models.ForeignKey(User, on_delete=models.CASCADE)
class ArticleSerializer(ModelSerializer):
user = StringRelatedField(read_only=True)
class Meta:
model = Article
fields = '__all__'
序列化的結(jié)果:
[
{
"id": 1,
"user": "更俗",
"title": "楚臣"
},
{
"id": 2,
"user": "更俗",
"title": "官場之風(fēng)流人生"
},
{
"id": 3,
"user": "更俗",
"title": "梟臣"
},
{
"id": 4,
"user": "天蠶土豆",
"title": "斗破蒼穹"
},
{
"id": 5,
"user": "天蠶土豆",
"title": "武動乾坤"
}
]
7.3 HyperlinkedRelatedField
使用
# urls.py
from django.contrib import admin
from django.urls import path
from my_app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('api/user/<int:pk>/', views.UserView.as_view(), name='user'),
path('api/articles/', views.ArticlesView.as_view()),
]
class ArticleSerializer(ModelSerializer):
user = HyperlinkedRelatedField(read_only=True, view_name='user')
class Meta:
model = Article
fields = '__all__'
class ArticlesView(APIView):
def get(self, request, *args, **kwargs):
quertset = Article.objects.all()
# 使用`HyperlinkedRelatedField`必須傳入環(huán)境參數(shù)`context={'request': request}`
articles_serializer = ArticleSerializer(instance=quertset, many=True, context={'request': request})
return Response(articles_serializer.data)
返回的結(jié)果如下
[
{
"id": 1,
"user": "http://127.0.0.1:8000/api/user/2/",
"title": "楚臣"
},
{
"id": 2,
"user": "http://127.0.0.1:8000/api/user/2/",
"title": "官場之風(fēng)流人生"
},
{
"id": 3,
"user": "http://127.0.0.1:8000/api/user/2/",
"title": "梟臣"
},
{
"id": 4,
"user": "http://127.0.0.1:8000/api/user/1/",
"title": "斗破蒼穹"
},
{
"id": 5,
"user": "http://127.0.0.1:8000/api/user/1/",
"title": "武動乾坤"
}
]
使用 HyperlinkedRelatedField
建議設(shè)置read_only = True
關(guān)鍵字參數(shù):
-
view_name
: 視圖函數(shù)名稱 -
lookup_field
: 生成url
舶替,從關(guān)系對象中的取數(shù)據(jù)的字段,默認(rèn)值為pk
-
lookup_url_kwarg
: 將取出的數(shù)據(jù)匹配url
路徑的字段,默認(rèn)值與lookup_field
相同杠园。
1??.當(dāng) url
中的路徑字段不為pk
時顾瞪,需要指定lookup_field
……
path('api/user/<str:username>/', views.UserView.as_view(), name='user'),
上面反向生成url
需要填補(bǔ)username
,而lookup_field
默認(rèn)為pk
,這個系統(tǒng)會從