Django REST Framework: การเพิ่มฟิลด์เพิ่มเติมใน ModelSerializer


148

ฉันต้องการทำให้เป็นอนุกรมรุ่น แต่ต้องการรวมเขตข้อมูลเพิ่มเติมที่ต้องทำการค้นหาฐานข้อมูลบางอย่างในอินสแตนซ์รูปแบบที่จะต่อเนื่อง:

class FooSerializer(serializers.ModelSerializer):
  my_field = ... # result of some database queries on the input Foo object
  class Meta:
        model = Foo
        fields = ('id', 'name', 'myfield')

วิธีที่ถูกต้องในการทำเช่นนี้คืออะไร? ฉันเห็นว่าคุณสามารถส่งต่อใน "บริบท" พิเศษเพื่อ serializer เป็นคำตอบที่ถูกต้องในฟิลด์เพิ่มเติมในพจนานุกรมบริบทหรือไม่ ด้วยวิธีการที่ตรรกะของการได้รับข้อมูลที่ฉันต้องการจะไม่เป็นตนเองมีความหมาย serializer my_fieldซึ่งเหมาะเป็นอย่างยิ่งตั้งแต่ทุกกรณีจะต้องต่อเนื่อง ที่อื่นในเอกสาร DRF serializers มันบอกว่า "เขตข้อมูลพิเศษสามารถสอดคล้องกับคุณสมบัติใด ๆ หรือ callable ในรูปแบบ" ฟิลด์พิเศษที่ฉันกำลังพูดถึงคืออะไร ฉันควรกำหนดฟังก์ชั่นในการFooกำหนดรูปแบบของที่คืนmy_fieldค่าและใน serializer ฉันขอ my_field เพื่อ callable ที่? มันมีลักษณะอย่างไร

ขอบคุณล่วงหน้ายินดีที่จะชี้แจงคำถามหากจำเป็น

คำตอบ:


225

ฉันคิดว่า SerializerMethodField คือสิ่งที่คุณกำลังมองหา:

class FooSerializer(serializers.ModelSerializer):
  my_field = serializers.SerializerMethodField('is_named_bar')

  def is_named_bar(self, foo):
      return foo.name == "bar" 

  class Meta:
    model = Foo
    fields = ('id', 'name', 'my_field')

http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield


19
เป็นไปได้หรือไม่ที่จะเพิ่มการตรวจสอบความถูกต้องให้กับฟิลด์ดังกล่าว คำถามของฉันคือ: วิธีการยอมรับค่า POST ที่กำหนดเองซึ่งสามารถตรวจสอบได้และกระบวนการในตัวจัดการ post_save ()?
Alp

20
โปรดทราบว่า SerializerMethodField เป็นแบบอ่านอย่างเดียวดังนั้นสิ่งนี้จะไม่ทำงานสำหรับ POST / PUT / PATCH ที่เข้ามา
สกอตต์

24
ใน DRF 3 จะเปลี่ยนเป็นfield_name = serializers.SerializerMethodField()และdef get_field_name(self, obj):
Chemical Programmer

1
คืออะไรfooเมื่อ def SerializerMethodField? เมื่อใช้ CreateAPIView foo ถูกเก็บไว้จากนั้นสามารถใช้วิธี is_named_bar () ได้ไหม?
244boy

ดังนั้นตามคำตอบนี้ถ้าคุณต้องการส่ง 12 เขตข้อมูลพิเศษให้ serializer ของคุณคุณต้องกำหนดวิธีการเฉพาะ 12 วิธีสำหรับแต่ละเขตข้อมูลที่เพิ่งส่งคืน foo.field_custom หรือไม่
AlxVallejo

41

คุณสามารถเปลี่ยนรูปแบบวิธีการเป็นคุณสมบัติและใช้ใน serializer ด้วยวิธีนี้

class Foo(models.Model):
    . . .
    @property
    def my_field(self):
        return stuff
    . . .

class FooSerializer(ModelSerializer):
    my_field = serializers.ReadOnlyField(source='my_field')

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

แก้ไข: ด้วยเฟรมเวิร์กพักผ่อนล่าสุด (ฉันลอง 3.3.3) คุณไม่จำเป็นต้องเปลี่ยนเป็นคุณสมบัติ วิธีการสร้างแบบจำลองจะทำงานได้ดี


ขอบคุณ @Wasil! ฉันไม่คุ้นเคยกับการใช้คุณสมบัติในโมเดล Django และไม่สามารถหาคำอธิบายที่ดีเกี่ยวกับความหมายได้ คุณสามารถอธิบาย? @ มัณฑนากรตกแต่งบ้านมีจุดประสงค์อะไร?
Neil

2
หมายความว่าคุณสามารถเรียกใช้เมธอดนี้ได้เช่นเดียวกับคุณสมบัติ: เช่นvariable = model_instance.my_fieldให้ผลลัพธ์แบบเดียวกับที่variable = model_instance.my_field()ไม่มีมัณฑนากร ยัง: stackoverflow.com/a/6618176/2198571
Wasil Sergejczyk

1
สิ่งนี้ไม่ทำงานอย่างน้อยใน Django 1.5.1 / djangorestframework == 2.3.10 ModelSerializer ไม่ได้รับความถูกต้องแม้ว่าจะอ้างถึงอย่างชัดเจนในแอตทริบิวต์ "Meta" Meta
ygneo

9
คุณต้องเพิ่มเขตข้อมูลลงใน serializer เพราะมันไม่มีตัวดัดแปลงจริง : my_field = serializers.Field (source = 'my_field')
Marius

2
source='my_field' ไม่ต้องการอีกต่อไปแล้วยกข้อยกเว้น
Guillaume Vincent

13

ด้วย Django Rest Framework เวอร์ชันล่าสุดคุณต้องสร้างวิธีในโมเดลของคุณด้วยชื่อของฟิลด์ที่คุณต้องการเพิ่ม ไม่ต้องการ@propertyและsource='field'เพิ่มข้อผิดพลาด

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

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

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

ถ้าฉันต้องการมีrequestวัตถุอยู่ภายใน def foo (ตัวเอง) ซึ่งสามารถแก้ไขค่าของ foo ได้ (ตัวอย่างการค้นหาจาก request.user)
saran3h

1
เกิดอะไรขึ้นถ้าค่าของ foo มาจากการร้องขอ?
saran3h

11

การตอบคำถามที่คล้ายกันของฉัน ( ที่นี่ ) อาจมีประโยชน์

หากคุณมีการกำหนดวิธีการแบบด้วยวิธีต่อไปนี้:

class MyModel(models.Model):
    ...

    def model_method(self):
        return "some_calculated_result"

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

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

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

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

10

ถ้าคุณต้องการอ่านและเขียนในเขตข้อมูลพิเศษของคุณคุณสามารถใช้ serializer ที่กำหนดเองใหม่ที่ขยาย serializers.Serializer และใช้มันเช่นนี้

class ExtraFieldSerializer(serializers.Serializer):
    def to_representation(self, instance): 
        # this would have the same as body as in a SerializerMethodField
        return 'my logic here'

    def to_internal_value(self, data):
        # This must return a dictionary that will be used to
        # update the caller's validation data, i.e. if the result
        # produced should just be set back into the field that this
        # serializer is set to, return the following:
        return {
          self.field_name: 'Any python object made with data: %s' % data
        }

class MyModelSerializer(serializers.ModelSerializer):
    my_extra_field = ExtraFieldSerializer(source='*')

    class Meta:
        model = MyModel
        fields = ['id', 'my_extra_field']

ฉันใช้สิ่งนี้ในฟิลด์ที่ซ้อนกันที่เกี่ยวข้องกับตรรกะที่กำหนดเอง


2

สิ่งนี้ใช้ได้ผลสำหรับฉัน หากเราต้องการเพิ่มเขตข้อมูลเพิ่มเติมใน ModelSerializer เราสามารถทำเช่นนี้ได้ด้านล่างและเขตข้อมูลสามารถกำหนดค่าบางค่าได้หลังจากการคำนวณการค้นหาบางอย่าง หรือในบางกรณีถ้าเราต้องการส่งพารามิเตอร์ในการตอบสนอง API

ใน model.py

class Foo(models.Model):
    """Model Foo"""
    name = models.CharField(max_length=30, help_text="Customer Name")



   **

ใน serializer.py

**

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

    @classmethod
    def get_retrieved_time(self, object):
        """getter method to add field retrieved_time"""
        return None

  class Meta:
        model = Foo
        fields = ('id', 'name', 'retrieved_time ')

**

หวังว่านี่จะช่วยคนได้

**


0

ตามที่นักเขียนโปรแกรมเคมีกล่าวไว้ในความคิดเห็นนี้ใน DRF ล่าสุดคุณสามารถทำสิ่งนี้ได้:

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

    def get_extra_field(self, foo_instance):
        return foo_instance.a + foo_instance.b

    class Meta:
        model = Foo
        fields = ('extra_field', ...)

แหล่งเอกสาร DRF

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.