Ratchaphon Hinsui

System and Software Engineer | Student at Kasetsart University | Classical Piano @ABRSM

การใช้งาน Serializer ใน django

ผู้พัฒนาเฟรมเวิร์ค 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')

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 instanceCode language: PHP (php)

เมื่อ deserializing data แล้ว คุณสามารถ .save() เพื่อที่จะ return object instance ขึ้นอยู่กับ data ที่ validate แล้ว

comment = serializer.save()
# .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)

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)

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

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])
    ...

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
  • เมื่อส่งข้อมูลไปยัง serializer instance
    • ข้อมูลที่ยังไม่ได้แก้ไขจะพร้อมใช้งานในชื่อ .initial_data
    • หากไม่มีการส่ง keyword argument data
      • attribute .initial_data จะไม่มีอยู่

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 หรือไม่ได้ระบุไว้ ควรจะเกิดอะไรต่อไป

ควรดำเนินการข้อใดต่อไปนี้

  1. ตั้งค่าความสัมพันธ์เป็น NULL ในฐานข้อมูล
  2. ลบ instance ที่เกี่ยวข้อง
  3. ละเว้นข้อมูลและปล่อยให้ instance เป็นเหมือนเดิม
  4. ยกเว้นข้อผิดพลาดในการตรวจสอบข้อมูล (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 instanceCode 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 สำหรับ model User
  • เมธอด create จะสร้าง instance ใหม่ของ User และ Profile ร่วมกัน
    • สร้าง instance ของ User ก่อน
    • บันทึก instance ของ User ลงฐานข้อมูล (user.save())
    • สร้าง instance ของ Profile โดยเชื่อมโยงกับ instance ของ User ที่สร้างไว้ก่อนหน้านี้
    • ตั้งค่า attribute อื่นๆ ของ Profile
    • บันทึก instance ของ Profile ลงฐานข้อมูล (profile.save())
    • ส่งคืน instance ของ User
  • 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
    • จากนั้นส่ง queryset หรือ list ของ object ที่ต้องการ serialize
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']

>>> 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']

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

 

 

วิธีที่ถูกต้องในการจัดการกับกรณีนี้คือการระบุฟิลด์โดยตรงใน 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_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

Your email address will not be published. Required fields are marked *