Получение значения внешнего ключа с помощью сериализаторов 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'}]

Как я могу получить обратное от сериализатора элементов, то есть:

[{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
если вы хотите получить все поля модели категории, создайте сериализатор категорий и
укажите
7
Я пытался сделать это, но получаю сообщение об ошибке Relational field must provide a 'queryset' argument, or set read_only='True'
ePascoal
или предоставив атрибут queryset, если вы хотите поддерживать создание / обновление, что-то вроде: category_name = serializers.RelatedField(source='category', queryset=Category.objects.all()) Я полагаю.
Стелиос,
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')

Более подробную информацию можно найти здесь: Основные аргументы полей сериализатора

Sayok88
источник
Но вы должны быть осторожны, потому что он должен выдать ошибку NoneType, если поле категории в модели Item установлено на blank = True
Desert Camel
29

Еще вы можете сделать следующее:

  • создать недвижимость в своем Item модели, которое возвращает имя категории и
  • выставить как файл 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__"
Suhailvs
источник
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',)

Используя Meta, read_only_fieldsмы можем точно указать, какие поля должны быть read_only. Затем нам нужно объявить foreignполе в Meta fields(лучше быть явным, как гласит мантра: zen of 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')
Жанабек
источник