Отримання значення зовнішнього ключа за допомогою серіалізаторів django-rest-framework


89

Я використовую фреймворк django rest для створення API. У мене є такі моделі:

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

Щоб створити серіалізатор для категорій, які я б зробив:

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

    class Meta:
        model = Category

... і це забезпечить мені:

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

Як би я дійшов до отримання зворотного від серіалізатора Item, тобто:

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

Я прочитав документи щодо зворотних зв'язків для решти фреймворку, але, схоже, це той самий результат, що і нереверсні поля. Мені щось не вистачає?

Відповіді:


85

Просто використовуйте відповідне поле без налаштування many=True.

Зверніть увагу, що, оскільки ви хочете, щоб вихідний результат був названий category_name, але фактичне поле є category, вам потрібно використовувати sourceаргумент у полі серіалізатора.

Нижче наведено результати, які вам потрібні ...

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

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

21
а як щодо отримання всіх полів моделі катагорії ??
AJ

12
якщо ви хочете отримати всі поля моделі категорії, зробіть серіалізатор категорії та вкажіть його в коді, як category_name = CategorySerliazer ()
Faizan Ali

7
Я намагався це зробити, але я отримую повідомлення про помилку Relational field must provide a 'queryset' argument, or set read_only='True'
ePascoal

або надання атрибуту набору запитів, якщо ви хочете підтримати створення / оновлення, щось на зразок: category_name = serializers.RelatedField(source='category', queryset=Category.objects.all()) я гадаю.
stelios

1
Якщо ви отримаєте, AssertionError:...використовуйте цю відповідь stackoverflow.com/a/44530606/5403449
Джош

86

У версії 3.6.3 DRF це спрацювало у мене

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

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

Більше інформації можна знайти тут: Основні аргументи полів серіалізатора


Але ви повинні бути готовими, оскільки воно повинно викликати помилку NoneType, якщо для поля категорії в елементі Item встановлено порожнє значення = True
Desert Camel

29

Інша справа, яку ви можете зробити, це:

  • створити властивість у своїй Itemмоделі, яка повертає назву категорії та
  • виставити це як a ReadOnlyField.

Ваша модель буде виглядати так.

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

Ваш серіалізатор виглядатиме так. Зверніть увагу, що серіалізатор автоматично отримає значення властивості category_nameмоделі, назвавши поле з тим самим іменем.

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

    class Meta:
        model = Item

18

у мене це добре працювало:

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

9

Працювали 08.08.2018 та над DRF версії 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',)

Використовуючи Мета, read_only_fieldsми можемо точно вказати, які поля слід читати тільки. Тоді нам потрібно оголосити foreignполе на Мета fields(краще бути явним, як йдеться в мантрі: zen з python ).


6

Просте рішення, source='category.name'де categoryє зовнішній ключ та .nameйого атрибут.

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

Це рішення краще, оскільки немає необхідності визначати вихідну модель. Але ім'я поля серіалізатора має збігатися з ім'ям поля зовнішнього ключа

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

    class Meta:
        model = Item
        fields = ('id', 'name', 'category')
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.