Django rest framework serializando muchos a muchos campos


84

¿Cómo serializo un campo de muchos a muchos en una lista de algo y los devuelvo a través del marco de descanso? En mi ejemplo a continuación, trato de devolver la publicación junto con una lista de etiquetas asociadas.

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

Con la ayuda de @Brian, logro enumerar los elementos en este formulario: "etiquetas": [{"nombre": "etiqueta1"}]. Me gustaría simplificarlo a la lista, es posible: "etiquetas": ["etiqueta1", "etiqueta2", ...]
kengcc

2
use `tags = serializers.SlugRelatedField (many = True, read_only = True, slug_field = 'title', // tag's fireld que desea mostrar allow_null = True)` en PostSerializers
M. Dhaouadi

Respuestas:


105

Necesitará un TagSerializer, cuyo class Metatiene model = Tag. Una vez TagSerializercreado, modifique PostSerializercon many=Truepara una ManyToManyFieldrelación:

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

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

La respuesta es para DRF 3


¡¡¡funciona!!! : D ¿Alguna idea de cómo convertir este serializador en una lista separada por comas? clase TagSerializer (serializadores.ModelSerializer): clase Meta: modelo = Campos de etiqueta = ('nombre')
kengcc

1
Ahora mismo, obtengo: "etiquetas": [{"nombre": "etiqueta1"}] Me gustaría simplificarlo a: "etiquetas": ["etiqueta1", "etiqueta2", ...]
kengcc

tags = serializers.ListField (fuente = 'etiqueta'). Esto le dará la lista de la representación str de cada objeto de la etiqueta
Sachin Gupta

2
¿Qué sucede si desea poder actualizar la etiqueta a través de la publicación? (por ejemplo, no read_only) Tengo un comportamiento extraño cuando elimino el read_only e intento PATCHAR una actualización en el campo de la etiqueta (
aparece

1
La read_only=Trueparte se explica aquí: django-rest-framework.org/api-guide/relations/…
Pavel Vergeev

25

Esto es lo que hice, supongamos que un libro puede tener más de un autor y un autor puede tener más de un libro: En modelo:

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)

En serializadores:

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

¿Alguna idea de cómo podemos crear autores al crear una entidad de libro?
Kishan Mehta

2
Sí, se debe hacer en la clase Vistas, por favor publique otra pregunta si desea una respuesta más detallada
Jesús Almaral - Hackaprende

8

Agregar a la respuesta de @ Brian "etiquetas": [{"nombre": "etiqueta1"}] se puede simplificar a "etiquetas": ["etiqueta1", "etiqueta2", ...] de esta manera:

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

Más información aquí: https://www.django-rest-framework.org/api-guide/relations/#custom-relational-fields


4

Esto funciona para mi.

tag = TagSerializer(source="tag", read_only=True, many=True)

4

Django 2.0

Para el campo de muchos a muchos, si desea uno específico:

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

Porque get_topics_listpodrías simplificar areturn list(instance.topics.values_list('desc', flat=True))
bdoubleu

2

En el serializador en el método init , puede pasar el conjunto de consultas al campo y rest_framework validar los identificadores en ese conjunto de consultas

1) primero amplíe su serializador desde los serializadores.

class YourSerializer(serializers.ModelSerializer):

2) incluir el campo en la metaclase

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

3) en el método init:

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

Puede limitar el conjunto de consultas para ese campo bajo cualquier argumento usando filtro o excluir como lo hace normalmente. En caso de que desee incluir todos, simplemente use .objects.all ()


1

El valor predeterminado ModelSerializerusa claves primarias para las relaciones. Sin embargo, puede generar fácilmente representaciones anidadas utilizando el Meta depthatributo:

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

Como se menciona en la documentación :

La depthopción debe establecerse en un valor entero que indique la profundidad de las relaciones que deben atravesarse antes de volver a una representación plana.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.