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.