Django Rest Framework - วิธีเพิ่มฟิลด์ที่กำหนดเองใน ModelSerializer


89

ฉันสร้างModelSerializerและต้องการเพิ่มฟิลด์ที่กำหนดเองซึ่งไม่ใช่ส่วนหนึ่งของโมเดลของฉัน

ฉันพบคำอธิบายเพื่อเพิ่มช่องพิเศษที่นี่และฉันได้ลองทำสิ่งต่อไปนี้:

customField = CharField(source='my_field')

เมื่อฉันเพิ่มฟิลด์นี้และเรียกvalidate()ใช้ฟังก์ชันของฉันฟิลด์นี้ไม่ได้เป็นส่วนหนึ่งของattrdict attrมีฟิลด์โมเดลทั้งหมดที่ระบุยกเว้นฟิลด์เพิ่มเติม ดังนั้นฉันจึงไม่สามารถเข้าถึงช่องนี้ในการตรวจสอบความถูกต้องที่เขียนทับได้ฉันจะทำได้หรือไม่?

เมื่อฉันเพิ่มฟิลด์นี้ในรายการฟิลด์ดังนี้:

class Meta:
    model = Account
    fields = ('myfield1', 'myfield2', 'customField')

จากนั้นฉันได้รับข้อผิดพลาดเนื่องจากcustomFieldไม่ใช่ส่วนหนึ่งของโมเดลของฉัน - สิ่งที่ถูกต้องเพราะฉันต้องการเพิ่มสำหรับซีเรียลไลเซอร์นี้เท่านั้น

มีวิธีใดในการเพิ่มฟิลด์ที่กำหนดเองหรือไม่?


คุณช่วยขยายใน "แต่เมื่อฟิลด์ของฉันไม่อยู่ในรายการฟิลด์โมเดลที่ระบุในซีเรียลไลเซอร์มันไม่ได้เป็นส่วนหนึ่งของ validate () attr-dictionary" ฉันไม่แน่ใจว่ามันชัดเจนมาก
Tom Christie

นอกจากนี้ "มันบ่น - ถูกต้อง - ฉันไม่มีฟิลด์ customField ในโมเดลของฉัน" คุณสามารถอธิบายได้อย่างชัดเจนเกี่ยวกับข้อยกเว้นที่คุณเห็น - ขอบคุณ! :)
Tom Christie

ฉันอัปเดตโพสต์ของฉันและหวังว่าตอนนี้จะชัดเจนขึ้น ฉันแค่อยากรู้ว่าฉันจะเพิ่มฟิลด์ที่ไม่ใช่ส่วนหนึ่งของโมเดลของฉันได้อย่างไร ...
รอน


คำตอบ:


63

คุณกำลังทำในสิ่งที่ถูกต้องยกเว้นสิ่งนั้น CharField (และช่องอื่น ๆ ที่พิมพ์) เป็นช่องที่เขียนได้

ในกรณีนี้คุณต้องการเพียงช่องอ่านอย่างเดียวธรรมดา ๆ ดังนั้นให้ใช้:

customField = Field(source='get_absolute_url')

4
ขอบคุณ แต่ฉันต้องการฟิลด์ที่เขียนได้ ฉันส่งโทเค็นผู้ใช้ที่ระบุผู้ใช้ของฉัน แต่ในโมเดลของฉันฉันมีผู้ใช้ไม่ใช่โทเค็น ดังนั้นฉันต้องการส่งโทเค็นและ "แปลง" ไปยังวัตถุของผู้ใช้ผ่านแบบสอบถาม
รอน

สิ่งต่อไปคือแหล่งที่มานั้นต้องการกำหนดเป้าหมายแอตทริบิวต์ model ใช่ไหม? ในกรณีของฉันฉันไม่มีแอตทริบิวต์ที่จะชี้ไปที่
รอน

ฉันไม่เข้าใจบิตของผู้ใช้ / โทเค็นของความคิดเห็น แต่ถ้าคุณต้องการรวมฟิลด์ในซีเรียลไลเซอร์ที่ถูกถอดออกก่อนที่คุณจะกู้คืนในอินสแตนซ์โมเดลคุณสามารถใช้.validate()วิธีลบแอตทริบิวต์ได้ ดู: django-rest-framework.org/api-guide/serializers.html#validation นั่นคือสิ่งที่คุณต้องการแม้ว่าฉันจะไม่เข้าใจกรณีการใช้งานจริงๆหรือทำไมคุณถึงต้องการฟิลด์ที่เขียนได้ซึ่งเชื่อมโยงกับ a คุณสมบัติอ่านอย่างเดียวget_absolute_url ?
Tom Christie

ลืมเรื่องที่get_absolute_urlฉันเพิ่งคัดลอกและวางจากเอกสาร ฉันต้องการเพียงช่องที่เขียนได้ตามปกติซึ่งฉันสามารถเข้าถึงได้ในไฟล์validate(). ฉันแค่เดาว่าฉันต้องการsourceคุณสมบัติ ...
รอน

ที่เข้าท่ากว่า :) ค่าควรถูกส่งผ่านเพื่อตรวจสอบความถูกต้องดังนั้นฉันจะตรวจสอบอีกครั้งว่าคุณกำลังสร้างอินสแตนซ์ซีเรียลไลเซอร์อย่างไรและมีการระบุค่านั้นจริงหรือไม่
Tom Christie

85

ในความเป็นจริงมีวิธีแก้ปัญหาโดยไม่ต้องสัมผัสเลยทุกรุ่น คุณสามารถใช้SerializerMethodFieldซึ่งอนุญาตให้คุณเสียบวิธีใดก็ได้กับตัวต่ออนุกรมของคุณ

class FooSerializer(ModelSerializer):
    foo = serializers.SerializerMethodField()

    def get_foo(self, obj):
        return "Foo id: %i" % obj.pk

6
ตามที่ OP กล่าวไว้ในความคิดเห็นนี้พวกเขาต้องการฟิลด์ที่เขียนได้ซึ่งSerializerMethodFieldไม่ใช่
esmail

14

... เพื่อความชัดเจนหากคุณมี Model Method ที่กำหนดไว้ดังต่อไปนี้:

class MyModel(models.Model):
    ...

    def model_method(self):
        return "some_calculated_result"

คุณสามารถเพิ่มผลลัพธ์ของการเรียกวิธีการดังกล่าวไปยังเครื่องอนุกรมของคุณได้ดังนี้:

class MyModelSerializer(serializers.ModelSerializer):
    model_method_field = serializers.CharField(source='model_method')

ps เนื่องจากฟิลด์ที่กำหนดเองไม่ใช่ฟิลด์ในโมเดลของคุณโดยทั่วไปคุณจะต้องทำให้เป็นแบบอ่านอย่างเดียวดังนี้:

class Meta:
    model = MyModel
    read_only_fields = (
        'model_method_field',
        )

3
จะทำอย่างไรถ้าฉันต้องการให้เขียนได้?
Csaba Toth

1
@Csaba: คุณเพียงแค่จะต้องเขียนบันทึกที่กำหนดเองและตะขอลบสำหรับเนื้อหาเพิ่มเติม: ดู "บันทึกและตะขอลบ" ภายใต้ "วิธีการ" ( ที่นี่ ) คุณจะต้องเขียนกำหนดเองperform_create(self, serializer), ,perform_update(self, serializer) perform_destroy(self, instance)
Lindauson

13

ที่นี่ตอบคำถามของคุณ คุณควรเพิ่มลงในบัญชีรุ่นของคุณ:

@property
def my_field(self):
    return None

ตอนนี้คุณสามารถใช้:

customField = CharField(source='my_field')

แหล่งที่มา: https://stackoverflow.com/a/18396622/3220916


6
ฉันเคยใช้วิธีนี้เมื่อมันสมเหตุสมผล แต่การเพิ่มโค้ดพิเศษลงในโมเดลสำหรับสิ่งที่ใช้สำหรับการเรียก API ที่เฉพาะเจาะจงเท่านั้น
Andy Baker

1
คุณสามารถเขียนแบบจำลองพร็อกซีสำหรับ
ashwoods

10

เพื่อแสดงให้ผมได้ข้อผิดพลาดกับself.author.full_name Fieldมันทำงานร่วมกับReadOnlyField:

class CommentSerializer(serializers.HyperlinkedModelSerializer):
    author_name = ReadOnlyField(source="author.full_name")
    class Meta:
        model = Comment
        fields = ('url', 'content', 'author_name', 'author')

6

ด้วย Django Rest Framework เวอร์ชันล่าสุดคุณต้องสร้างเมธอดในโมเดลของคุณด้วยชื่อฟิลด์ที่คุณต้องการเพิ่ม

class Foo(models.Model):
    . . .
    def foo(self):
        return 'stuff'
    . . .

class FooSerializer(ModelSerializer):
    foo = serializers.ReadOnlyField()

    class Meta:
        model = Foo
        fields = ('foo',)

4

ฉันกำลังมองหาวิธีแก้ปัญหาสำหรับการเพิ่มฟิลด์แบบกำหนดเองที่สามารถเขียนได้ลงในเครื่องอนุกรมโมเดล ฉันพบข้อนี้ซึ่งยังไม่ครอบคลุมในคำตอบของคำถามนี้

ดูเหมือนว่าคุณจำเป็นต้องเขียน Serializer อย่างง่ายของคุณเอง

class PassThroughSerializer(serializers.Field):
    def to_representation(self, instance):
        # This function is for the direction: Instance -> Dict
        # If you only need this, use a ReadOnlyField, or SerializerField
        return None

    def to_internal_value(self, data):
        # This function is for the direction: Dict -> Instance
        # Here you can manipulate the data if you need to.
        return data

ตอนนี้คุณสามารถใช้ Serializer นี้เพื่อเพิ่มฟิลด์ที่กำหนดเองให้กับ ModelSerializer

class MyModelSerializer(serializers.ModelSerializer)
    my_custom_field = PassThroughSerializer()

    def create(self, validated_data):
        # now the key 'my_custom_field' is available in validated_data
        ...
        return instance

นอกจากนี้ยังใช้งานได้หาก Model MyModelมีคุณสมบัติที่เรียกใช้จริงmy_custom_fieldแต่คุณต้องการละเว้น validators


ดังนั้นจึงใช้ไม่ได้ถ้า my_custom_field ไม่ใช่คุณสมบัติของ MyModel ใช่ไหม ฉันได้รับข้อผิดพลาดฟิลด์ serializer อาจตั้งชื่อไม่ถูกต้องและไม่ตรงกับแอตทริบิวต์หรือคีย์ใด ๆ บนMyModelอินสแตนซ์
Sandeep Balagopal

3

หลังจากอ่านคำตอบทั้งหมดที่นี่ข้อสรุปของฉันคือมันเป็นไปไม่ได้ที่จะทำสิ่งนี้อย่างหมดจด คุณต้องเล่นสกปรกและทำอะไรบางอย่างที่ไม่น่าสนใจเช่นการสร้างฟิลด์ write_only จากนั้นแทนที่validateand to_representationmethod นี่คือสิ่งที่ใช้ได้ผลสำหรับฉัน:

class FooSerializer(ModelSerializer):

    foo = CharField(write_only=True)

    class Meta:
        model = Foo
        fields = ["foo", ...]

    def validate(self, data):
        foo = data.pop("foo", None)
        # Do what you want with your value
        return super().validate(data)

    def to_representation(self, instance):
        data = super().to_representation(instance)
        data["foo"] = whatever_you_want
        return data
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.