Abrufen eines Fremdschlüsselwerts mit Django-Rest-Framework-Serialisierern

83

Ich verwende das Django Rest Framework, um eine API zu erstellen. Ich habe folgende Modelle:

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

So erstellen Sie einen Serializer für die Kategorien:

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

    class Meta:
        model = Category

... und das würde mich versorgen mit:

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

Wie würde ich vorgehen, um das Gegenteil von einem Item-Serializer zu erhalten, dh:

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

Ich habe die Dokumente zu umgekehrten Beziehungen für das Rest-Framework gelesen, aber das scheint das gleiche Ergebnis zu sein wie die nicht umgekehrten Felder. Vermisse ich etwas Offensichtliches?

Höllenpforte
quelle

Antworten:

82

Verwenden Sie einfach ein verwandtes Feld ohne Einstellung many=True.

Beachten Sie, dass Sie auch das Argument für das Serializer-Feld verwenden müssen, da die Ausgabe benannt werden category_namesoll, das tatsächliche Feld jedoch .categorysource

Das Folgende sollte Ihnen die Ausgabe geben, die Sie benötigen ...

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

    class Meta:
        model = Item
        fields = ('id', 'name', 'category_name')
Tom Christie
quelle
21
Was ist mit dem Wiederauffinden aller Bereiche des Kategorienmodells?
AJ
12
Wenn Sie alle Felder des Kategoriemodells abrufen möchten, erstellen Sie den Kategorieserialisierer und lassen Sie ihn in einem Code wie category_name = CategorySerliazer ()
Faizan Ali
7
Ich habe versucht, dies zu tun, aber ich Relational field must provide a 'queryset' argument, or set read_only='True'
erhalte
oder ein Queryset-Attribut bereitstellen, wenn Sie das Erstellen / Aktualisieren unterstützen möchten, etwa: category_name = serializers.RelatedField(source='category', queryset=Category.objects.all()) Ich nehme an.
Stelios
1
Wenn Sie erhalten, AssertionError:...verwenden Sie diese Antwort stackoverflow.com/a/44530606/5403449
Josh
83

In der DRF-Version 3.6.3 hat dies bei mir funktioniert

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

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

Weitere Informationen finden Sie hier: Kernargumente der Serializer-Felder

Sayok88
quelle
Aber Sie müssen vorsichtig sein, denn es sollte einen NoneType-Fehler auslösen, wenn das Kategoriefeld im Item-Modell auf leer gesetzt ist = True
Desert Camel
29

Sie können auch Folgendes tun:

  • Erstellen Sie in Ihrem ItemModell eine Eigenschaft , die den Kategorienamen und zurückgibt
  • Belichten Sie es als ReadOnlyField.

Ihr Modell würde so aussehen.

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

Ihr Serializer würde so aussehen. Beachten Sie, dass der Serializer automatisch den Wert der category_nameModelleigenschaft erhält, indem er das Feld mit demselben Namen benennt.

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

    class Meta:
        model = Item
hsebastian
quelle
16

das hat bei mir gut funktioniert:

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

Arbeitete am 08/08/2018 und an der DRF-Version 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',)

Mit dem Meta können read_only_fieldswir genau deklarieren, welche Felder read_only sein sollen. Dann müssen wir das foreignFeld auf der Meta deklarieren fields(besser explizit sein, wie das Mantra sagt: Zen of Python ).

John Moutafis
quelle
5

Einfache Lösung, source='category.name'bei der categoryes sich um einen Fremdschlüssel und .namedessen Attribut handelt.

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__"
Anurag Misra
quelle
0

Diese Lösung ist besser, da das Quellmodell nicht definiert werden muss. Der Name des Serializer-Felds sollte jedoch mit dem Namen des Fremdschlüsselfelds übereinstimmen

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

    class Meta:
        model = Item
        fields = ('id', 'name', 'category')
zshanabek
quelle