Recuperar un valor de clave externa con serializadores django-rest-framework


83

Estoy usando el marco de descanso de django para crear una API. Tengo los siguientes modelos:

class Category(models.Model):
    name = models.CharField(max_length=100)

    def __unicode__(self):
        return self.name


class Item(models.Model):
    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category, related_name='items')

    def __unicode__(self):
        return self.name

Para crear un serializador para las categorías, haría:

class CategorySerializer(serializers.ModelSerializer):
    items = serializers.RelatedField(many=True)

    class Meta:
        model = Category

... y esto me proporcionaría:

[{'items': [u'Item 1', u'Item 2', u'Item 3'], u'id': 1, 'name': u'Cat 1'},
 {'items': [u'Item 4', u'Item 5', u'Item 6'], u'id': 2, 'name': u'Cat 2'},
 {'items': [u'Item 7', u'Item 8', u'Item 9'], u'id': 3, 'name': u'Cat 3'}]

¿Cómo haría para obtener lo contrario de un serializador de artículos, es decir:

[{u'id': 1, 'name': 'Item 1', 'category_name': u'Cat 1'},
{u'id': 2, 'name': 'Item 2', 'category_name': u'Cat 1'},
{u'id': 3, 'name': 'Item 3', 'category_name': u'Cat 1'},
{u'id': 4, 'name': 'Item 4', 'category_name': u'Cat 2'},
{u'id': 5, 'name': 'Item 5', 'category_name': u'Cat 2'},
{u'id': 6, 'name': 'Item 6', 'category_name': u'Cat 2'},
{u'id': 7, 'name': 'Item 7', 'category_name': u'Cat 3'},
{u'id': 8, 'name': 'Item 8', 'category_name': u'Cat 3'},
{u'id': 9, 'name': 'Item 9', 'category_name': u'Cat 3'}]

He leído los documentos sobre relaciones inversas para el resto del marco, pero parece ser el mismo resultado que los campos no inversos. ¿Me estoy perdiendo algo obvio?

Respuestas:


82

Simplemente use un campo relacionado sin configurar many=True.

Tenga en cuenta que también porque desea que se nombre la salida category_name, pero el campo real sí lo es category, debe usar el sourceargumento en el campo del serializador.

Lo siguiente debería darle el resultado que necesita ...

class ItemSerializer(serializers.ModelSerializer):
    category_name = serializers.RelatedField(source='category', read_only=True)

    class Meta:
        model = Item
        fields = ('id', 'name', 'category_name')

21
¿Qué hay de recuperar todos los campos del modelo de categoría?
AJ

12
si desea recuperar todos los campos del modelo de categoría, haga el serializador de categoría y
téngalo

7
Intenté hacer esto pero recibo un error Relational field must provide a 'queryset' argument, or set read_only='True'
ePascoal

o proporcionar un atributo de conjunto de consultas si desea admitir la creación / actualización, algo como: category_name = serializers.RelatedField(source='category', queryset=Category.objects.all()) supongo.
stelios

1
Si obtiene, AssertionError:...use esta respuesta stackoverflow.com/a/44530606/5403449
Josh

83

En la versión 3.6.3 de DRF esto funcionó para mí

class ItemSerializer(serializers.ModelSerializer):
    category_name = serializers.CharField(source='category.name')

    class Meta:
        model = Item
        fields = ('id', 'name', 'category_name')

Puede encontrar más información aquí: Argumentos principales de los campos del serializador


Pero debe tener cuidado porque debería arrojar un error NoneType si el campo de categoría en el modelo de artículo está en blanco = True
Desert Camel

29

Otra cosa que puede hacer es:

  • cree una propiedad en su Itemmodelo que devuelva el nombre de la categoría y
  • exponerlo como un ReadOnlyField.

Tu modelo se vería así.

class Item(models.Model):
    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category, related_name='items')

    def __unicode__(self):
        return self.name

    @property
    def category_name(self):
        return self.category.name

Su serializador se vería así. Tenga en cuenta que el serializador obtendrá automáticamente el valor de la category_namepropiedad del modelo al nombrar el campo con el mismo nombre.

class ItemSerializer(serializers.ModelSerializer):
    category_name = serializers.ReadOnlyField()

    class Meta:
        model = Item

16

Esto funcionó bien para mi:

class ItemSerializer(serializers.ModelSerializer):
    category_name = serializers.ReadOnlyField(source='category.name')
    class Meta:
        model = Item
        fields = "__all__"

9

Trabajó el 08/08/2018 y en DRF versión 3.8.2:

class ItemSerializer(serializers.ModelSerializer):
    category_name = serializers.ReadOnlyField(source='category.name')

    class Meta:
        model = Item
        read_only_fields = ('id', 'category_name')
        fields = ('id', 'category_name', 'name',)

Usando el Meta read_only_fieldspodemos declarar exactamente qué campos deben ser read_only. Entonces necesitamos declarar el foreigncampo en el Meta fields(mejor ser explícito como dice el mantra: zen de python ).


5

Solución simple source='category.name'donde categoryestá la clave externa y .namesu atributo.

from rest_framework.serializers import ModelSerializer, ReadOnlyField
from my_app.models import Item

class ItemSerializer(ModelSerializer):
    category_name = ReadOnlyField(source='category.name')

    class Meta:
        model = Item
        fields = "__all__"

0

Esta solución es mejor porque no es necesario definir el modelo de origen. Pero el nombre del campo del serializador debe ser el mismo que el nombre del campo de clave externa

class ItemSerializer(serializers.ModelSerializer):
    category = serializers.SlugRelatedField(read_only=True, slug_field='title')

    class Meta:
        model = Item
        fields = ('id', 'name', 'category')
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.