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?

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

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


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

16

das hat bei mir gut funktioniert:

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

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


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__"

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')
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.