Django REST Framework: adicionando campo adicional ao ModelSerializer


148

Quero serializar um modelo, mas quero incluir um campo adicional que exija que algumas pesquisas de banco de dados na instância do modelo sejam serializadas:

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

Qual é a maneira certa de fazer isso? Vejo que você pode passar no "contexto" extra para o serializador, é a resposta certa para passar no campo adicional em um dicionário de contexto? Com essa abordagem, a lógica de obter o campo necessário não seria independente da definição do serializador, o que é ideal, pois todas as instâncias serializadas precisarão my_field. Em outra parte da documentação dos serializadores DRF, diz que "campos extras podem corresponder a qualquer propriedade ou que podem ser chamados no modelo". Campos extras são do que estou falando? Devo definir uma função na Foodefinição de modelo que retorne my_fieldvalor e, no serializador, conecto meu_campo àquele que pode ser chamado? Como é isso?

Agradecemos antecipadamente, prazer em esclarecer a pergunta, se necessário.

Respostas:


225

Eu acho que SerializerMethodField é o que você está procurando:

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
é possível adicionar validação a esses campos? minha pergunta é: como aceitar valores POST personalizados que podem ser validados e processados ​​no manipulador post_save ()?
Alp

20
Observe que SerializerMethodField é somente leitura, portanto, isso não funcionará para POST / PUT / PATCH de entrada.
Scott A

24
No DRF 3, é alterado para field_name = serializers.SerializerMethodField()edef get_field_name(self, obj):
Chemical Programmer

1
o que é fooquando def um SerializerMethodField? Ao usar CreateAPIView, o foo foi armazenado e pode usar o método is_named_bar ()?
244boy #

Portanto, com base nesta resposta, se você quiser passar 12 campos extras para o serializador, precisará definir 12 métodos específicos para cada campo que retornem apenas foo.field_custom?
AlxVallejo 13/04/19

41

Você pode alterar o método do modelo para propriedade e usá-lo no serializador com esta abordagem.

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

Edit: Com versões recentes do resto framework (eu tentei 3.3.3), você não precisa mudar para propriedade. O método de modelo funcionará bem.


Obrigado @Wasil! Não estou familiarizado com o uso de propriedades nos modelos do Django e não consigo encontrar uma boa explicação do que isso significa. Você pode explicar? Qual é o sentido do decorador @property aqui?
Neil

2
significa que você pode chamar esse método como uma propriedade: ou seja, variable = model_instance.my_fieldfornece o mesmo resultado que variable = model_instance.my_field()sem o decorador. Também: stackoverflow.com/a/6618176/2198571
Wasil Sergejczyk

1
Isso não está funcionando, pelo menos no Django 1.5.1 / djangorestframework == 2.3.10. O ModelSerializer não está obtendo a propriedade, mesmo se explicitamente referido no atributo Meta "fields".
Ygneo 23/03

9
você precisa adicionar o campo ao serializador, porque não é um campo de campo real : my_field = serializers.Field (source = 'my_field')
Marius

2
source='my_field' não é mais necessário e gera uma exceção #
Guillaume Vincent

13

Com a última versão do Django Rest Framework, você precisa criar um método no seu modelo com o nome do campo que deseja adicionar. Não há necessidade @propertye source='field'gera um erro.

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

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

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

e se eu quiser ter um requestobjeto dentro de def foo (self) que possa modificar o valor de foo? (exemplo uma pesquisa baseada request.user)
saran3h

1
E se o valor de foo vier do pedido?
saran3h

11

Minha resposta a uma pergunta semelhante ( aqui ) pode ser útil.

Se você tiver um método de modelo definido da seguinte maneira:

class MyModel(models.Model):
    ...

    def model_method(self):
        return "some_calculated_result"

Você pode adicionar o resultado da chamada do método mencionado ao serializador da seguinte maneira:

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

ps Como o campo personalizado não é realmente um campo no seu modelo, você geralmente deseja torná-lo somente leitura, assim:

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

10

se você quiser ler e gravar em seu campo extra, poderá usar um novo serializador personalizado, que estenda os serializadores.Serializador, e use-o assim

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

eu uso isso em campos aninhados relacionados com alguma lógica personalizada


2

Isso funcionou para mim . Se queremos apenas adicionar um campo adicional no ModelSerializer, podemos fazê-lo como abaixo e também ao campo pode ser atribuído algum valor após alguns cálculos de pesquisa. Ou, em alguns casos, se quisermos enviar os parâmetros na resposta da API.

Em model.py

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



   **

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

**

Espero que isso possa ajudar alguém

**


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.