web-dev-qa-db-fra.com

Récupération d'une valeur de clé étrangère avec les sérialiseurs Django-rest-framework

J'utilise le framework Django rest pour créer une API. J'ai les modèles suivants:

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

Pour créer un sérialiseur pour les catégories que je ferais:

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

    class Meta:
        model = Category

... et cela me fournirait:

[{'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'}]

Comment puis-je obtenir l'inverse d'un sérialiseur d'élément, à savoir:

[{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'}]

J'ai lu la documentation sur relations inverses pour le reste de la structure, mais cela semble être le même résultat que les champs non inversés. Est-ce que je manque quelque chose d'évident?

51
hellsgate

Il suffit d'utiliser un champ associé sans définir many=True.

Notez aussi que vous voulez que la sortie nommée category_name, mais le champ réel est category, vous devez utiliser l’argument source sur le champ du sérialiseur.

Ce qui suit devrait vous donner la sortie dont vous avez besoin ...

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

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

Dans la version actuelle de DRF (3.6.3) cela a fonctionné pour moi

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

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

Une autre chose que vous pouvez faire est de:

  • créer une propriété dans votre modèle Item qui renvoie le nom de la catégorie et
  • l'exposer comme un ReadOnlyField.

Votre modèle ressemblerait à ceci.

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

Votre sérialiseur ressemblerait à ceci. Notez que le sérialiseur obtiendra automatiquement la valeur du category_name propriété du modèle en nommant le champ avec le même nom.

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

    class Meta:
        model = Item
20
hsebastian

cA marchait bien pour moi:

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

Travaillé le 08/08/2018 et sur la version 3.8.2 de DRF:

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

Utiliser le Meta read_only_fields _ nous pouvons déclarer exactement quels champs doivent être read_only. Ensuite, nous devons déclarer le champ foreign sur la méta fields (mieux vaut être explicite comme le mantra le dit: zen of python ).

4
John Moutafis

Solution simple source='category.name'category est la clé étrangère et .name c'est l'attribut.

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__
2
Anurag Misra