Фреймворк Django rest, сериализующий поля многие ко многим

84

Как мне сериализовать поле «многие ко многим» в список чего-либо и вернуть их через структуру отдыха? В моем примере ниже я пытаюсь вернуть сообщение вместе со списком связанных с ним тегов.

models.py

class post(models.Model):
    tag = models.ManyToManyField(Tag)
    text = models.CharField(max_length=100)

serializers.py

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ("text", "tag"??)

views.py

class PostViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
Kengcc
источник
С помощью @Brian мне удалось перечислить элементы в этой форме: "tags": [{"name": "tag1"}]. Я хотел бы упростить его до перечисления, возможно ли это: «теги»: [«тег1», «тег2», ...]
kengcc
2
используйте `tags = serializers.SlugRelatedField (many = True, read_only = True, slug_field = 'title', // fireld тега, который вы хотите показать allow_null = True)` в PostSerializers
М. Дхауади

Ответы:

105

Вам понадобится тот TagSerializer, у кого class Metaесть model = Tag. После TagSerializerсоздания измените PostSerializerс many=Trueпомощью ManyToManyFieldотношения:

class PostSerializer(serializers.ModelSerializer):
    tag = TagSerializer(read_only=True, many=True)

    class Meta:
        model = Post
        fields = ('tag', 'text',)

Ответ для DRF 3

Брайан
источник
оно работает!!! : D Есть идеи, как превратить этот сериализатор в список, разделенный запятыми? class TagSerializer (serializers.ModelSerializer): class Meta: model = Tag fields = ('name')
kengcc
1
Прямо сейчас я получаю: "tags": [{"name": "tag1"}]. Я хотел бы упростить это до: "tags": ["tag1", "tag2", ...]
kengcc
теги = сериализаторы.ListField (источник = 'тег'). Это даст вам список str- представления каждого объекта тега
Сачин Гупта
2
Что, если вы хотите иметь возможность обновлять тег через Post? (например, not read_only) У меня странное поведение, когда я убираю read_only и пытаюсь ПАТЧИРОВАТЬ обновление в поле тега (я получаю сообщение об уже существующем теге)
getup8
1
read_only=TrueЧасть объясняется здесь: django-rest-framework.org/api-guide/relations/...
Павел Отряд Вергеев
25

Вот что я сделал. Предположим, что у книги может быть более одного автора, а у автора может быть более одной книги: О модели:

class Author(models.Model):
    name = models.CharField(max_length=100, default="")
    last_name = models.IntegerField(default=0)

class Book(models.Model):
    authors = models.ManyToManyField(Author, related_name="book_list", blank=True)
    name = models.CharField(max_length=100, default="")
    published = models.BooleanField(default=True)

О сериализаторах:

class BookSerializer(serializers.ModelSerializer):
    authors = serializers.PrimaryKeyRelatedField(queryset=Author.objects.all(), many=True)

    class Meta:
        model = Book
        fields = ('id', 'name', 'published', 'authors')


class AuthorSerializer(serializers.ModelSerializer):
    book_list = BookSerializer(many=True, read_only=True)

    class Meta:
        model = Author
        fields = ('id', 'name', 'last_name', 'book_list')
Хесус Альмарал - Hackaprende
источник
Любая идея, как мы можем создавать авторов при создании сущности Book ??
Кишан Мехта,
2
Да, это должно быть сделано в классе Views, пожалуйста,
задайте
8

Добавление к ответу @Brian "тегов": [{"name": "tag1"}] можно упростить до "тегов": ["tag1", "tag2", ...] следующим образом:

class PostSerializer(serializers.ModelSerializer):
    tag = TagSerializer(read_only=True, many=True)

    class Meta:
        ...

class TagSerializer(serializers.RelatedField):

     def to_representation(self, value):
         return value.name

     class Meta:
        model = Tag

Подробнее здесь: https://www.django-rest-framework.org/api-guide/relations/#custom-relational-fields

конфеты
источник
4

У меня это работает.

tag = TagSerializer(source="tag", read_only=True, many=True)
Windsooon
источник
4

Django 2.0

Для поля многие ко многим, если вы хотите конкретное:

class QuestionSerializer(serializers.ModelSerializer):

    topics_list = serializers.SerializerMethodField()

    def get_topics_list(self, instance):
        names = []
        a = instance.topics.get_queryset()
        for i in a:
            names.append(i.desc)
        return names
    class Meta:
        model = Question
        fields = ('topics_list',)
пользователь5299374
источник
Чтобы get_topics_listвы могли упроститьreturn list(instance.topics.values_list('desc', flat=True))
bdoubleu
2

В сериализаторе метода инициализации вы можете передать набор запросов в поле, а rest_framework проверить идентификаторы в этом наборе запросов.

1) сначала расширите свой сериализатор из сериализаторов.ModelSerializer

class YourSerializer(serializers.ModelSerializer):

2) включить поле в мета-класс

class YourSerializer(serializers.ModelSerializer):
  class Meta:
        fields = (..., 'your_field',)

3) в методе инициализации:

def __init__(self, *args, **kwargs):
    super(YourSerializer, self).__init__(*args, **kwargs)
    self.fields['your_field].queryset = <the queryset of your field>

Вы можете ограничить набор запросов для этого поля любым аргументом, используя фильтр или исключить, как обычно. Если вы хотите включить все, просто используйте .objects.all ()

йё
источник
1

По умолчанию ModelSerializerдля отношений используются первичные ключи. Однако вы можете легко создавать вложенные представления, используя Meta depthатрибут:

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ("text", "tag")
        depth = 1 

Как упоминалось в документации :

Параметр depthдолжен быть установлен в целочисленное значение, которое указывает глубину отношений, которые должны быть пройдены перед возвратом к плоскому представлению.

Мохамед Али Мимуни
источник