Django REST Framework: ModelSerializer'a ek alan ekleme


149

Bir modeli serileştirmek istiyorum, ancak serileştirilecek model örneğinde bazı veritabanı aramaları yapmayı gerektiren ek bir alan eklemek istiyorum:

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

Bunu yapmanın doğru yolu nedir? Ben serileştiriciye ekstra "bağlam" geçebilirsiniz görüyorum, bir bağlam sözlüğünde ek alana geçmek için doğru cevap mı? Bu yaklaşımla, ihtiyacım olan alanı elde etme mantığı, serileştirilmiş her örneğin ihtiyaç duyacağı için ideal olan serileştirici tanımı ile kendi kendine yetmez my_field. DRF serileştiricileri belgelerinde başka yerlerde "fazladan alanlar herhangi bir özelliğe karşılık gelebilir veya modelde çağrılabilir" yazıyor . Fazladan bahsettiğim alanlar mı? FooModel tanımında my_fielddeğer döndüren bir işlev tanımlamalı mıyım ve serileştiricide my_field'ı bu çağrılabilire bağladım mı? Bu neye benziyor?

Şimdiden teşekkürler, gerekirse soruyu açıklığa kavuşturmaktan mutluluk duyarız.

Yanıtlar:


226

SerializerMethodField aradığınız şey olduğunu düşünüyorum:

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
bu alanlara doğrulama eklemek mümkün müdür? benim sorum: post_save () işleyicide doğrulanabilen özel POST değerlerini ve süreçlerini nasıl kabul edebilirim?
Alp

20
SerializerMethodField salt okunur olduğundan, gelen POST / PUT / PATCH için çalışmaz.
Scott A

24
DRF 3, bu şekilde değiştirilir field_name = serializers.SerializerMethodField()vedef get_field_name(self, obj):
Kimyasal Programcı

1
ne foozaman def bir SerializerMethodField def? CreateAPIView kullanıldığında, foo is_named_bar () yöntemini kullanabilir mi?
244boy

Yani, bu cevaba dayanarak, serileştiricinize 12 ekstra alan söylemek istiyorsanız, her alan için sadece foo.field_custom?
AlxVallejo

41

Model yönteminizi özelliğe değiştirebilir ve bu yaklaşımla seri hale getiricide kullanabilirsiniz.

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',)

Düzenleme: dinlenme çerçevesi (3.3.3 denedim) son sürümleri ile, özellik değiştirmek gerekmez. Model yöntemi iyi çalışır.


Teşekkürler @Wasil! Django modellerinde özelliklerin kullanımına aşina değilim ve ne anlama geldiğinin iyi bir açıklamasını bulamıyorum. Açıklayabilir misin? @Property dekoratörünün anlamı nedir?
Neil

2
bu yöntemi bir özellik gibi çağırabileceğiniz anlamına gelir: yani dekoratör olmadan variable = model_instance.my_fieldaynı sonucu verir variable = model_instance.my_field(). ayrıca: stackoverflow.com/a/6618176/2198571
Wasil Sergejczyk

1
Bu en azından Django 1.5.1 / djangorestframework == 2.3.10'da çalışmıyor. ModelSerializer, açıkça "fields" Meta özniteliğinde açıkça belirtilse bile, özelliği almıyor.
ygneo

9
gerçek bir model alanı olmadığından alanı serileştiriciye eklemeniz gerekir : my_field = serializers.Field (source = 'my_field')
Marius

2
source='my_field' artık gerekmiyor ve istisna oluşturmuyor
Guillaume Vincent

13

Django Rest Framework'ün son sürümünde, modelinizde eklemek istediğiniz alanın adıyla bir yöntem oluşturmanız gerekir. Bir hataya gerek yok @propertyve source='field'bir hata yükseltmek.

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

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

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

requestfoo'nun değerini değiştirebilecek def foo (self) içinde nesne olmasını istersem ne olur ? (örnek bir request.user tabanlı arama)
saran3h

1
Ya foo değeri talepten geliyorsa?
saran3h

11

Benzer bir soruya ( burada ) yanıt vermem yararlı olabilir.

Aşağıdaki şekilde tanımlanmış bir Model Yönteminiz varsa:

class MyModel(models.Model):
    ...

    def model_method(self):
        return "some_calculated_result"

Bahsedilen yöntemi çağırmanın sonucunu serileştiricinize şu şekilde ekleyebilirsiniz:

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

ps Özel alan gerçekten modelinizdeki bir alan olmadığından, genellikle aşağıdaki gibi salt okunur yapmak istersiniz:

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

10

fazladan alanınızda okumak ve yazmak istiyorsanız, serileştiricileri genişleten yeni bir özel serileştirici kullanabilirsiniz.

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

Bu bazı özel mantık ile ilgili iç içe alanlarda kullanın


2

Bu benim için çalıştı . Sadece içine ek bir alan eklemek istiyorsakModelSerializer, bunu aşağıdaki gibi yapabiliriz ve ayrıca bazı arama hesaplamalarından sonra alana bazı val atanabilir. Veya bazı durumlarda, parametreleri API yanıtında göndermek istiyorsak.

Model.py içinde

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

Serializer.py içinde

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

Umarım bu birine yardımcı olabilir.


0

As Kimya Programer söyledi Bu yoruma , DRF son içinde aynen bu şekilde yapabilirsiniz:

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 dokümanları kaynağı

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.