
ผู้พัฒนาเฟรมเวิร์ค Django REST Framework มองเห็นถึงความต้องการในการขยายความสามารถของ Serializer แต่ก็ยอมรับว่าเป็นโจทย์ที่ท้าทาย ต้องอาศัยการออกแบบอย่างละเอียด
Serializer ทำหน้าที่แปลงข้อมูลเชิงซ้อน เช่น ชุดข้อมูล (querysets) และข้อมูลของโมเดล ไปเป็นรูปแบบข้อมูลพื้นฐานของ Python (native Python datatypes) เพื่อให้แปลงเป็น JSON, XML หรือฟอร์แม็ตอื่นๆ ได้ง่ายดาย นอกจากนี้ Serializer ยังรองรับการแปลงข้อมูลกลับ (deserialization) โดยตรวจสอบข้อมูลขาเข้าก่อนแปลงเป็นโครงสร้างข้อมูลที่ซับซ้อนอีกครั้ง
Serializer ใน REST framework มีแนวคิดการทำงานคล้ายกับ Form และ ModelForm ใน Django โดยมี Serializer class เป็นคลาสพื้นฐานที่ให้ความยืดหยุ่นสูงในการควบคุมข้อมูลส่งออก (output) ส่วน ModelSerializer เป็นทางลัดสำหรับสร้าง Serializer ที่ทำงานกับข้อมูลโมเดลและชุดข้อมูล (querysets)
Declaring Serializers
เริ่มจากการ สร้าง simple object
from datetime import datetime
class Comment:
def __init__(self, email, content, created=None):
self.email = email
self.content = content
self.created = created or datetime.now()
comment = Comment(email='leila@example.com', content='foo bar')
เราจะสามารถสร้าง serializer ในการ serialize และ deserialize data เป็น Comment object ได้
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
Serializing objects
คุณสามารถใช้ CommentSerializer
ในการ serialize comment หรือ list ของ comments
serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
Code language: PHP (php)
ณ จุดนี้ เราจะได้ instance model เป็นประเภทข้อมูล type ของ django ที่สามารถนำไปแปลงเป็น json ได้
from rest_framework.renderers import JSONRenderer
json = JSONRenderer().render(serializer.data)
json
# b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'
Code language: PHP (php)
Deserializing objects
Deserialization จะคล้ายกับ serializing ก่อนอื่นจะแยกหรือเปลี่ยนเป็น python datatype ก่อน
import io
from rest_framework.parsers import JSONParser
stream = io.BytesIO(json)
data = JSONParser().parse(stream)
Code language: JavaScript (javascript)
จากนั้นเราจะกู้คืนประเภทข้อมูลดั้งเดิมเหล่านั้นลงใน dictionary ที่เช็คแล้วว่าถูกต้อง
serializer = CommentSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# {'content': 'foo bar', 'email': 'leila@example.com', 'created': datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)}
Code language: PHP (php)
Saving instances
ถ้าต้องการ return object instance หลังจาก validate data แล้ว สามารถ implement ได้ หนึ่ง หรือ ทั้งคู่ .create()
และ .update()
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
def create(self, validated_data):
return Comment(**validated_data)
def update(self, instance, validated_data):
instance.email = validated_data.get('email', instance.email)
instance.content = validated_data.get('content', instance.content)
instance.created = validated_data.get('created', instance.created)
return instance
หาก instance object ที่ใส่เข้าไปใน serialize ตรงกับ model การสามารถ implement method create เป็นแบบ ตัวอย่างนี้ได้
def create(self, validated_data):
return Comment.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.email = validated_data.get('email', instance.email)
instance.content = validated_data.get('content', instance.content)
instance.created = validated_data.get('created', instance.created)
instance.save()
return instance
Code language: PHP (php)
เมื่อ deserializing data แล้ว คุณสามารถ .save()
เพื่อที่จะ return object instance ขึ้นอยู่กับ data ที่ validate แล้ว
comment = serializer.save()
เรียกใช้ .save()
จะ create new instance หรือ จะ update instance ที่มีอยู่แล้ว ขึ้นอยู่ว่าผ่านการ validate แล้วหรือไม่
# .save() will create a new instance.
serializer = CommentSerializer(data=data)
# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
Code language: PHP (php)
ทั้ง .create()
และ .update()
เป็น method optional คุณสามารถ implement ทั้งคู่นี้ได้
ตาม use case ที่ต้องการ
Passing additional attributes to .save()
บางครั้ง คุณอาจจะอยากส่งข้อมูลเพิ่มเติมเข้าไปก่อน save ไม่จำเป็นต้องเอามาจาก serialize คุณสามารถทำได้โดยการรวม arguments เพิ่มก่อน save เช่น
serializer.save(owner=request.user)
additional keyword arguments ใดๆก็ตาม ที่เพิ่มมาจะถูก เพิ่มใน validated_data
เมื่อ
.create()
หรือ .update()
ถูกเรียกใช
Overriding .save()
directly.
ในบาง case .create()
และ .update()
ชื่อยังไม่มีความหมายมาก ตัวอย่างเช่น contact form อาจะไม่ได้ create new instance แต่ ต้องการ send email ถึงคนอื่น
ใน case นี้ คุณอาจจะ override .save()
โดยตรง
class ContactForm(serializers.Serializer):
email = serializers.EmailField()
message = serializers.CharField()
def save(self):
email = self.validated_data['email']
message = self.validated_data['message']
send_email(from=email, message=message)
Validation
เมื่อ deserializing data คุณจะต้อง เรียก function is_valid()
ก่อน ที่จะพยายาม access ข้อมูลที่ validated แล้ว หรือ save object ถ้า validation errors เกิดขึ้น .errors
จะ เก็บ dictionary ที่เป็น result ของ error message
serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'email': ['Enter a valid e-mail address.'], 'created': ['This field is required.']}
Code language: PHP (php)
key ใน dictionary จะเป็น field name และ value จะเป็น list ของ string เกี่ยวกับ error message อาจจะมี key non_field_errors
ซึ่งจะเป็นlist ข้อผิดพลาดทั่วไปของการตรวจสอบข้อมูล ชื่อของ key นี้สามารถปรับแต่งได้ด้วยค่าตั้งค่า NON_FIELD_ERRORS_KEY
ของ REST framework
เมื่อ deserializing list ของ items errors จะ returned ไปเป็น list ของ dictionary ที่สื้อถึง deserialized items
Raising an exception on invalid data
.is_valid()
method มี option ในการ raise_exception
นั่นเพราะว่า มันสามารถ raise serializers.ValidationError
exception ถ้ามัน validate แล้ว error
Exception เหลานี้ จะถูกจัดการ automatic โดย default exception handler ของ REST Framework มันจะ return HTTP 400 Bad Request
โดยปกติ
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)
Code language: PHP (php)
Field-level validation
คุณสามารถ custom field-level validation ได้โดยการ เพิ่ม .validate_<field_name>
method ใน Serializer
subclass จะคล้ายกับ .clean_<field_name>
method ใน Django Forms
method นี้จะรับ argument คือ value ที่ ต้องการ validation
method นี้ควร return value ที่ validated แล้ว หรือจะ raise serializers.ValidationError
from rest_framework import serializers
class BlogPostSerializer(serializers.Serializer):
title = serializers.CharField(max_length=100)
content = serializers.CharField()
def validate_title(self, value):
"""
Check that the blog post is about Django.
"""
if 'django' not in value.lower():
raise serializers.ValidationError("Blog post is not about Django")
return value
Note ถ้า
<field_name>
ที่เขียน validate ไว้ แต่ใน parameterrequired=False
ขั้นตอนนี้จะไม่เกิดขึ้น
Object-level validation
การตรวจสอบข้อมูลที่ต้องใช้ข้อมูลจากหลายฟิลด์ ให้ใช้ method .validate()
ใน Serializer
method นี้รับ argument เดียว นั่นคือ dictionary field method นี้ควร return value ที่ validated แล้ว หรือ raise serializers.ValidationError
from rest_framework import serializers
class EventSerializer(serializers.Serializer):
description = serializers.CharField(max_length=100)
start = serializers.DateTimeField()
finish = serializers.DateTimeField()
def validate(self, data):
"""
Check that start is before finish.
"""
if data['start'] > data['finish']:
raise serializers.ValidationError("finish must occur after start")
return data
Validators
ในfield เดียวๆบน serializer สามารถ เพิ่ม validator ได้ดังตัวอย่าง
def multiple_of_ten(value):
if value % 10 != 0:
raise serializers.ValidationError('Not a multiple of ten')
class GameRecord(serializers.Serializer):
score = serializers.IntegerField(validators=[multiple_of_ten])
...
Serializer Class สามารถ เพิ่ม validator ที่ใช้ซ้ำกับข้อมูลทั้งหมดด้วยการประกาศใว้ใน tag Meta
class EventSerializer(serializers.Serializer):
name = serializers.CharField()
room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
date = serializers.DateField()
class Meta:
# Each room only has one event per day.
validators = [
UniqueTogetherValidator(
queryset=Event.objects.all(),
fields=['room_number', 'date']
)
]
เพิ่มเติม validators documentation
Accessing the initial data and instance
- เมื่อส่ง object หรือ queryset เริ่มต้นไปยัง serializer instance
- object นั้นจะพร้อมใช้งานในชื่อ
.instance
- หากไม่มีการส่ง object เริ่มต้น
- attribute
.instance
จะเป็นNone
- attribute
- object นั้นจะพร้อมใช้งานในชื่อ
- เมื่อส่งข้อมูลไปยัง serializer instance
- ข้อมูลที่ยังไม่ได้แก้ไขจะพร้อมใช้งานในชื่อ
.initial_data
- หากไม่มีการส่ง keyword argument
data
- attribute
.initial_data
จะไม่มีอยู่
- attribute
- ข้อมูลที่ยังไม่ได้แก้ไขจะพร้อมใช้งานในชื่อ
Partial updates
โดยปกติ serializer จะต้อง pass value ทุก required field ไม่ยั้นจะเกิดข้อผิดพลาด
อย่างไรก็ตาม คุณสามารถใช้ argument partial
เพื่อรองรับการอัปเดตแบบบางส่วนได้
# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)
Code language: PHP (php)
Dealing with nested objects
จากตัวอย่างที่แล้ว เราจัดการกับ objects ที่มีแค่ simple datatype แต่บางครั้งเราก็ต้องจัดการกับ complex objects บางที attribute ของ object ไม่ได้อยู่ใน simple datatype ได้แก่ string,date หรือ integer
Serializer
class ตัวมันเองก็เป็น Field อันนึง นั่นสามารถ ทำ relationship object type nested ในอีก Serializer
นึ่งได้
class UserSerializer(serializers.Serializer):
email = serializers.EmailField()
username = serializers.CharField(max_length=100)
class CommentSerializer(serializers.Serializer):
user = UserSerializer()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
ถ้าอยากให้ nested Serialize ไม่จำเป็นต้อง required หรือ รับ ค่า None
ได้ ให้ใส่ required=False
ที่ nested serializer
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False) # May be an anonymous user.
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
ถ้า nested Serialize ควรจะเป็น list of items ให้ใส่ many=True
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False)
edits = EditItemSerializer(many=True) # A nested list of 'edit' items.
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
Writable nested representations
เมื่อต้องจัดการกับ การแสดงข้อมูล ที่ซับซ้อน ที่จะ support deserializing data error ใดๆ ก็ตามจะ อยู่ใน field name ของ object นั้นๆ
serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'user': {'email': ['Enter a valid e-mail address.']}, 'created': ['This field is required.']}
Code language: PHP (php)
โดยทั่วไป .validated_data
จะ เพิ่ม nested data structure ด้วย
Writing .create()
methods for nested representations
ถ้าต้องการให้ serialize สามารถ save nested object ได้จะต้องเขียน .create()
และ .update()
ให้ save multiple objects
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ['username', 'email', 'profile']
def create(self, validated_data):
profile_data = validated_data.pop('profile')
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, **profile_data)
return user
Writing .update()
methods for nested representations
เพื่อที่จะ update คุณต้องระวังเกียวกับการ handle update relationship ด้วย เช่น ถ้า data จาก relationship เป็น None
หรือไม่ได้ระบุไว้ ควรจะเกิดอะไรต่อไป
ควรดำเนินการข้อใดต่อไปนี้
- ตั้งค่าความสัมพันธ์เป็น
NULL
ในฐานข้อมูล - ลบ instance ที่เกี่ยวข้อง
- ละเว้นข้อมูลและปล่อยให้ instance เป็นเหมือนเดิม
- ยกเว้นข้อผิดพลาดในการตรวจสอบข้อมูล (validation error)
def update(self, instance, validated_data):
profile_data = validated_data.pop('profile')
# Unless the application properly enforces that this field is
# always set, the following could raise a `DoesNotExist`, which
# would need to be handled.
profile = instance.profile
instance.username = validated_data.get('username', instance.username)
instance.email = validated_data.get('email', instance.email)
instance.save()
profile.is_premium_member = profile_data.get(
'is_premium_member',
profile.is_premium_member
)
profile.has_support_contract = profile_data.get(
'has_support_contract',
profile.has_support_contract
)
profile.save()
return instance
Code language: PHP (php)
เพราะ พฤติกรรมของ nested create และ update สามารถเกิดความกำกวมได้ อาจะทำให้ต้องการ complex dependencies ระหว่าง related models REST framework 3 required ให้เขียน method ให้ชัดเจน ModelSerializer
.create()
และ .update()
method เองก็ไม่ได้ลองรับการ writable nested
แต่อย่างไรก็ตามมี third-party packages ในการ ทำสิ่งนี้อยู่ DRF Writable Nested
Handling saving related instances in model manager classes
ทางเลือก ในการ save multiple related instances คือการเขียน custom model Manager
อีกทางเลือกหนึ่งนอกเหนือจากการบันทึก instance ที่เกี่ยวข้องหลายรายการใน serializer คือ การเขียน custom model manager class ที่จัดการการสร้าง instance ที่ถูกต้อง
ตัวอย่าง:
สมมติว่าเราต้องการให้แน่ใจว่า instance ของ User
และ instance ของ Profile
ถูกสร้างร่วมกันเป็นคู่เสมอ เราอาจเขียน custom manager class ดังนี้
class UserManager(models.Manager):
...
def create(self, username, email, is_premium_member=False, has_support_contract=False):
user = User(username=username, email=email)
user.save()
profile = Profile(
user=user,
is_premium_member=is_premium_member,
has_support_contract=has_support_contract
)
profile.save()
return user
อธิบาย:
UserManager
เป็น custom model manager class สำหรับ modelUser
- เมธอด
create
จะสร้าง instance ใหม่ของUser
และProfile
ร่วมกัน- สร้าง instance ของ
User
ก่อน - บันทึก instance ของ
User
ลงฐานข้อมูล (user.save()
) - สร้าง instance ของ
Profile
โดยเชื่อมโยงกับ instance ของUser
ที่สร้างไว้ก่อนหน้านี้ - ตั้งค่า attribute อื่นๆ ของ
Profile
- บันทึก instance ของ
Profile
ลงฐานข้อมูล (profile.save()
) - ส่งคืน instance ของ
User
- สร้าง instance ของ
- Class
UserManager
ช่วยให้มั่นใจได้ว่า instance ของUser
และProfile
ถูกสร้างพร้อมกันเสมอ
ข้อดี:
- การใช้ custom model manager class ช่วยให้ code อ่านง่ายและเข้าใจง่ายขึ้น
- ลดความซับซ้อนในการจัดการการสร้าง instance ที่เกี่ยวข้องภายใน serializer
การปรับแต่ง serializer:
serializer class สามารถปรับแต่งให้ใช้ประโยชน์จาก custom model manager class ได้ ดังนี้
def create(self, validated_data):
return User.objects.create(
username=validated_data['username'],
email=validated_data['email'],
is_premium_member=validated_data['profile']['is_premium_member'],
has_support_contract=validated_data['profile']['has_support_contract']
)
Code language: PHP (php)
อธิบาย:
- เมธอด
create
ของ serializer class เรียกใช้เมธอดcreate
จากUser.objects
(ซึ่งอ้างถึงUserManager
) - ข้อมูลที่จำเป็นสำหรับการสร้าง instance ทั้งสองถูกส่งต่อไปยังเมธอด
create
ของUserManager
แหล่งข้อมูลเพิ่มเติม:
- https://docs.djangoproject.com/en/5.0/topics/db/managers/ (เอกสารเกี่ยวกับ model manager ของ Django)
- บล็อกโพสต์เกี่ยวกับการใช้ model และ manager class (ไม่ได้แนบลิ้งค์ตามเงื่อนไข)
Dealing with multiple objects
Serializer class สามารถ serialize หรือ deserialize รายการข้อมูลได้มากกว่าหนึ่งรายการ
Serializing multiple objects
- serializer class รองรับการแปลงข้อมูลจาก model instance เป็น JSON หรือแปกลับกัน
- กรณีต้องการ serialize รายการของ object (เช่น queryset)
- แทนที่จะส่ง object เดียว
- ให้ตั้งค่า flag
many=True
ขณะสร้าง serializer instance
- ให้ตั้งค่า flag
- จากนั้นส่ง queryset หรือ list ของ object ที่ต้องการ serialize
- แทนที่จะส่ง object เดียว
from rest_framework import serializers
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ('id', 'title', 'author')
queryset = Book.objects.all()
serializer = BookSerializer(queryset, many=True)
print(serializer.data)
result
[
{"id": 0, "title": "The electric kool-aid acid test", "author": "Tom Wolfe"},
{"id": 1, "title": "If this is a man", "author": "Primo Levi"},
{"id": 2, "title": "The wind-up bird chronicle", "author": "Haruki Murakami"}
]
Code language: JSON / JSON with Comments (json)
Deserializing multiple objects
โดยปกติ พฤติกรรม ในการ Deserialize multiple
การ deserialize ข้อมูลหลายรายการ
พฤติกรรมเริ่มต้น:
- serializer รองรับการสร้าง object หลายรายการจากข้อมูลที่ deserialize
- แต่ไม่รองรับการอัปเดต object หลายรายการ
รายละเอียดเพิ่มเติม:
- กรณีต้องการรองรับหรือปรับแต่งการ serialize หรือ deserialize ข้อมูลหลายรายการ
- ควรศึกษาเอกสารเกี่ยวกับ ListSerializer เพิ่มเติม ListSerializer
Including extra context
มีบาง case ที่คุณจำเป็นต้องมีการจัดเตรียม บริบทเพิ่มเติม นอกเหนือจาก object ที่กำลัง serialize อยู่
กรณีทั่วไปกรณีหนึ่งคือ หากคุณใช้ซีเรียลไลเซอร์ที่มีความสัมพันธ์แบบไฮเปอร์ลิงก์ ซึ่งต้องการให้ซีเรียลไลเซอร์เข้าถึงคำขอปัจจุบัน เพื่อให้สามารถสร้าง URL ที่มีคุณสมบัติครบถ้วนได้อย่างเหมาะสม
คุณสามารถจัดเตรียมบริบทเพิ่มเติมได้ตามใจชอบโดยการส่งอาร์กิวเมนต์บริบทเมื่อสร้างอินสแตนซ์ของซีเรียลไลเซอร์ ตัวอย่างเช่น:
serializer = AccountSerializer(account, context={'request': request})
serializer.data
# {'id': 6, 'owner': 'denvercoder9', 'created': datetime.datetime(2013, 2, 12, 09, 44, 56, 678870), 'details': 'http://example.com/accounts/6/details'}
Code language: PHP (php)
Context สามารถใช้ได้ภายใน serialized เช่น .to_representation()
แบบกำหนดเองโดยเข้าถึงผ่าน self.context
ModelSerializer
บ่อยครั้งที่คุณต้องการคลาส serializer ที่ตรงกับการกำหนด Django model อย่างใกล้ชิด
คลาส ModelSerializer มีทางลัดที่ช่วยให้คุณสร้างคลาส Serializer โดยอัตโนมัติด้วยฟิลด์ที่ตรงกับฟิลด์ของ Model
คลาส ModelSerializer เหมือนกับคลาส Serializer ทั่วไป ยกเว้นว่า:
มันจะสร้างชุดของฟิลด์ให้คุณโดยอัตโนมัติตามโมเดล
มันจะสร้างตัวตรวจสอบความถูกต้องสำหรับ serializer โดยอัตโนมัติ เช่นตัวตรวจสอบ unique_together
มันมีการติดตั้งฟังก์ชัน .create()
และ .update()
เริ่มต้นอย่างง่าย
การประกาศ ModelSerializer ดูเหมือนดังนี้:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
โดยค่าเริ่มต้น ฟิลด์ของโมเดลทั้งหมดในคลาสจะถูกแมปไปยังฟิลด์ของ serializer ที่สอดคล้องกัน
ความสัมพันธ์ใด ๆ เช่น foreign key ในโมเดลจะถูกแมปไปยัง PrimaryKeyRelatedField ความสัมพันธ์แบบย้อนกลับจะไม่ถูกรวมโดยค่าเริ่มต้นเว้นแต่จะรวมไว้อย่างชัดเจนตามที่ระบุไว้ในเอกสาร serializer relations
การตรวจสอบ ModelSerializer คลาส Serializer สร้างสตริงการแสดงผลแบบ verbose ที่เป็นประโยชน์ซึ่งช่วยให้คุณตรวจสอบสถานะของฟิลด์ได้อย่างเต็มที่ สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อทำงานกับ ModelSerializers ที่คุณต้องการตรวจสอบชุดฟิลด์และตัวตรวจสอบความถูกต้องที่ถูกสร้างขึ้นโดยอัตโนมัติสำหรับคุณ
ในการทำเช่นนั้น ให้เปิด Django shell โดยใช้ python manage.py shell จากนั้นนำเข้า class serializer สร้างอินสแตนซ์ของมันและพิมพ์การแสดงผลของอ็อบเจ็กต์…
>>> from myapp.serializers import AccountSerializer
>>> serializer = AccountSerializer()
>>> print(repr(serializer))
AccountSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(allow_blank=True, max_length=100, required=False)
owner = PrimaryKeyRelatedField(queryset=User.objects.all())
Code language: PHP (php)
Specifying which fields to include
หากคุณต้องการใช้เฉพาะชุดย่อยของฟิลด์เริ่มต้นใน model serializer คุณสามารถทำได้โดยใช้ตัวเลือก fields หรือ exclude เช่นเดียวกับที่คุณทำกับ ModelForm ขอแนะนำอย่างยิ่งให้คุณตั้งค่าฟิลด์ทั้งหมดที่ควรถูก serialize อย่างชัดเจนโดยใช้แอตทริบิวต์ fields ซึ่งจะทำให้มีโอกาสน้อยที่จะเผยข้อมูลโดยไม่ตั้งใจเมื่อโมเดลของคุณเปลี่ยนแปลง
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
คุณยังสามารถตั้งค่าแอตทริบิวต์ fields เป็นค่าพิเศษ ‘all‘ เพื่อระบุว่าควรใช้ฟิลด์ทั้งหมดในโมเดล
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = '__all__'
คุณสามารถตั้งค่าแอตทริบิวต์ exclude เป็นรายการของฟิลด์ที่ไม่ต้องการให้รวมใน serializer
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
exclude = ['users']
ในตัวอย่างข้างต้น หากโมเดล Account มี 3 ฟิลด์ account_name, users, และ created จะส่งผลให้ฟิลด์ account_name และ created ถูก serialize
ชื่อในแอตทริบิวต์ fields และ exclude จะถูกแมปไปยังฟิลด์ในคลาสโมเดลตามปกติ
นอกจากนี้ชื่อในตัวเลือก fields สามารถแมปไปยังพร็อพเพอร์ตี้หรือเมธอดที่ไม่มีอาร์กิวเมนต์ซึ่งมีอยู่ในคลาสโมเดลได้
ตั้งแต่เวอร์ชัน 3.3.0 เป็นต้นไป จำเป็นต้องระบุหนึ่งในแอตทริบิวต์ fields หรือ exclude
Specifying nested serialization
ModelSerializer เริ่มต้นจะใช้คีย์หลักสำหรับความสัมพันธ์ แต่คุณยังสามารถสร้างการแสดงผลแบบ nested ได้อย่างง่ายดายโดยใช้ตัวเลือก depth:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
depth = 1
ตัวเลือก depth ควรถูกตั้งค่าเป็นค่าจำนวนเต็มที่ระบุระดับความลึกของความสัมพันธ์ที่ควรถูก traversal ก่อนที่จะกลับไปใช้การแสดงผลแบบ flat
หากคุณต้องการปรับแต่งวิธีการทำ serialization คุณจะต้องกำหนดฟิลด์ด้วยตนเอง
Specifying fields explicitly
คุณสามารถเพิ่มฟิลด์เพิ่มเติมให้กับ ModelSerializer หรือเขียนทับฟิลด์เริ่มต้นโดยการประกาศฟิลด์ในคลาส เช่นเดียวกับที่คุณทำกับคลาส Serializer
class AccountSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
groups = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Account
fields = ['url', 'groups']
ฟิลด์เพิ่มเติมสามารถสอดคล้องกับพร็อพเพอร์ตี้หรือ callable ใด ๆ ในโมเดล
Specifying read only fields
คุณอาจต้องการระบุฟิลด์หลายฟิลด์เป็นแบบอ่านอย่างเดียว แทนที่จะเพิ่มฟิลด์แต่ละฟิลด์โดยใช้แอตทริบิวต์ read_only=True คุณสามารถใช้ตัวเลือก Meta แบบทางลัด read_only_fields
ตัวเลือกนี้ควรเป็นรายการหรือลำดับของชื่อฟิลด์ และประกาศดังนี้:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
read_only_fields = ['account_name']
ฟิลด์ในโมเดลที่ตั้งค่า editable=False และฟิลด์ AutoField จะถูกตั้งค่าเป็นแบบอ่านอย่างเดียวโดยค่าเริ่มต้น และไม่จำเป็นต้องเพิ่มไปในตัวเลือก read_only_fields
หมายเหตุ: มีกรณีพิเศษที่ฟิลด์แบบอ่านอย่างเดียวเป็นส่วนหนึ่งของข้อจำกัด unique_together ในระดับโมเดล ในกรณีนี้ฟิลด์จะต้องการโดยคลาส serializer เพื่อยืนยันข้อจำกัด แต่ไม่ควรแก้ไขได้โดยผู้ใช้
วิธีที่ถูกต้องในการจัดการกับกรณีนี้คือการระบุฟิลด์โดยตรงใน serializer โดยให้ทั้งแอตทริบิวต์ read_only=True และ default=…
ตัวอย่างหนึ่งของกรณีนี้คือความสัมพันธ์แบบอ่านอย่างเดียวกับผู้ใช้ที่ได้รับการยืนยันการเข้าสู่ระบบในปัจจุบันซึ่งเป็น unique_together กับตัวระบุอื่น ในกรณีนี้คุณจะประกาศฟิลด์ user ดังนี้:
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
Code language: PHP (php)
โปรดตรวจสอบเอกสาร Validators สำหรับรายละเอียดเกี่ยวกับคลาส UniqueTogetherValidator และ CurrentUserDefault
Additional keyword arguments
นอกจากนี้ยังมีทางลัดที่ให้คุณระบุ keyword arguments เพิ่มเติมใด ๆ ในฟิลด์โดยใช้ตัวเลือก extra_kwargs เช่นเดียวกับในกรณีของ read_only_fields ซึ่งหมายความว่าคุณไม่จำเป็นต้องประกาศฟิลด์ใน serializer โดยตรง
ตัวเลือกนี้เป็นพจนานุกรมที่แมปชื่อฟิลด์ไปยังพจนานุกรมของ keyword arguments ตัวอย่างเช่น:
class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email', 'username', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User(
email=validated_data['email'],
username=validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
โปรดทราบว่า หากฟิลด์นั้นได้ถูกประกาศในคลาส serializer โดยตรงแล้ว ตัวเลือก extra_kwargs จะถูกละเว้น
Relational fields
เมื่อทำการ serialize อินสแตนซ์ของโมเดล มีหลายวิธีที่คุณอาจเลือกใช้เพื่อแสดงความสัมพันธ์ การแสดงผลเริ่มต้นสำหรับ ModelSerializer คือการใช้ primary key ของอินสแตนซ์ที่เกี่ยวข้อง
การแสดงผลทางเลือกอื่น ๆ รวมถึงการ serialize โดยใช้ไฮเปอร์ลิงก์ การ serialize แบบ nested representation แบบสมบูรณ์ หรือการ serialize ด้วยการแสดงผลแบบกำหนดเอง
สำหรับรายละเอียดเพิ่มเติมโปรดดูเอกสาร serializer relations
Customizing field mappings
คลาส ModelSerializer ยังมี API ที่คุณสามารถเขียนทับเพื่อเปลี่ยนแปลงวิธีการกำหนดฟิลด์ serializer โดยอัตโนมัติเมื่อสร้างอินสแตนซ์ของ serializer
ปกติแล้วหาก ModelSerializer ไม่สร้างฟิลด์ที่คุณต้องการโดยค่าเริ่มต้น คุณควรเพิ่มฟิลด์เหล่านั้นในคลาสโดยตรง หรือใช้คลาส Serializer ปกติแทน อย่างไรก็ตาม ในบางกรณีคุณอาจต้องการสร้างคลาสฐานใหม่ที่กำหนดวิธีการสร้างฟิลด์ serializer สำหรับโมเดลใดๆ
serializer_field_mapping การแมปฟิลด์ของ Django model ไปยังฟิลด์ของ REST framework serializer คุณสามารถเขียนทับการแมปนี้เพื่อเปลี่ยนแปลงฟิลด์ serializer เริ่มต้นที่ควรถูกใช้สำหรับฟิลด์ของแต่ละโมเดล
serializer_related_field พร็อพเพอร์ตี้นี้ควรเป็นคลาสฟิลด์ของ serializer ที่ใช้สำหรับฟิลด์ relational โดยค่าเริ่มต้น
สำหรับ ModelSerializer ค่านี้จะเป็น serializers.PrimaryKeyRelatedField
สำหรับ HyperlinkedModelSerializer ค่านี้จะเป็น serializers.HyperlinkedRelatedField
serializer_url_field คลาสฟิลด์ของ serializer ที่ควรถูกใช้สำหรับฟิลด์ url ใด ๆ บน serializer
ค่าเริ่มต้นคือ serializers.HyperlinkedIdentityField
serializer_choice_field คลาสฟิลด์ของ serializer ที่ควรถูกใช้สำหรับฟิลด์ choice ใด ๆ บน serializer
ค่าเริ่มต้นคือ serializers.ChoiceField
field_class และ field_kwargs API เมธอดต่อไปนี้จะถูกเรียกเพื่อกำหนดคลาสและ keyword arguments สำหรับแต่ละฟิลด์ที่ควรถูกเพิ่มใน serializer โดยอัตโนมัติ เมธอดเหล่านี้แต่ละเมธอดควรคืนค่าทูเพิลสองตัวของ (field_class, field_kwargs)
build_standard_field(self, field_name, model_field) ถูกเรียกเพื่อสร้างฟิลด์ serializer ที่แมปไปยังฟิลด์โมเดลมาตรฐาน
การติดตั้งเริ่มต้นจะคืนค่าคลาส serializer ตามแอตทริบิวต์ serializer_field_mapping
build_relational_field(self, field_name, relation_info) ถูกเรียกเพื่อสร้างฟิลด์ serializer ที่แมปไปยังฟิลด์ relational model
การติดตั้งเริ่มต้นจะคืนค่าคลาส serializer ตามแอตทริบิวต์ serializer_related_field
อาร์กิวเมนต์ relation_info เป็น named tuple ที่ประกอบด้วยพร็อพเพอร์ตี้ model_field, related_model, to_many และ has_through_model
build_nested_field(self, field_name, relation_info, nested_depth) ถูกเรียกเพื่อสร้างฟิลด์ serializer ที่แมปไปยังฟิลด์ relational model เมื่อมีการตั้งค่า depth
การติดตั้งเริ่มต้นจะสร้างคลาส nested serializer โดยอัตโนมัติตาม ModelSerializer หรือ HyperlinkedModelSerializer
nested_depth จะเป็นค่าของตัวเลือก depth ลบด้วยหนึ่ง
อาร์กิวเมนต์ relation_info เป็น named tuple ที่ประกอบด้วยพร็อพเพอร์ตี้ model_field, related_model, to_many และ has_through_model
build_property_field(self, field_name, model_class) ถูกเรียกเพื่อสร้างฟิลด์ serializer ที่แมปไปยังพร็อพเพอร์ตี้หรือเมธอดที่ไม่มีอาร์กิวเมนต์บนคลาสโมเดล
การติดตั้งเริ่มต้นจะคืนค่าคลาส ReadOnlyField
build_url_field(self, field_name, model_class) ถูกเรียกเพื่อสร้างฟิลด์ serializer สำหรับฟิลด์ url ของ serializer เอง การติดตั้งเริ่มต้นจะคืนค่าคลาส HyperlinkedIdentityField
build_unknown_field(self, field_name, model_class)
ถูกเรียกเมื่อชื่อฟิลด์ไม่ได้แมปไปยังฟิลด์โมเดลหรือพร็อพเพอร์ตี้ของโมเดล การติดตั้งเริ่มต้นจะเกิดข้อผิดพลาด แม้ว่าคลาสย่อยอาจปรับแต่งพฤติกรรมนี้ได้
Leave a Reply