使用嵌套的外键序列化ManyToManyField

时间:2020-06-07 11:48:07

标签: django django-rest-framework

我需要序列化一个从多到多相关但每个属性类型分组的项目的属性。

模型

    from django.db import models


    class AtributoTipo(models.Model):
        nombre = models.CharField(max_length=40)
        slug = models.SlugField(max_length=40, unique=True)


    class Atributo(models.Model):
        tipo = models.ForeignKey(AtributoTipo, on_delete=models.CASCADE)
        nombre = models.CharField(max_length=40)
        slug = models.SlugField(max_length=40)


    class Articulo(models.Model):
        codigo = models.CharField(max_length=18, unique=True, db_index=True)
        nombre = models.CharField(max_length=255)
        atributos = models.ManyToManyField(Atributo, related_name="articulos")

普通DRF序列化器:

    from rest_framework import serializers

    class AtributoTipoSerializer(serializers.ModelSerializer):
        class Meta:
            model = AtributoTipo
            fields = ["__all__"]


    class AtributoSerializer(serializers.ModelSerializer):
        tipo_slug = serializers.ReadOnlyField(source="tipo.slug")
        tipo_nombre = serializers.ReadOnlyField(source="tipo.nombre")
        class Meta:
            model = Atributo
            fields = ["__all__"]


    class ArticuloSerializer(serializers.ModelSerializer):
        atributos = AtributoSerializer(many=True, read_only=True) # the dude
        class Meta:
            model = Articulo
            fields = ["__all__"]

这是没有任何怪异方法的序列化程序的结果:

    {
        "id": 44906,
        "codigo": "DE0058751",
        "atributos": [
            {
                "id": 15107,
                "tipo": 76,
                "tipo_slug": "talla",
                "tipo_nombre": "Talla",
                "nombre": "39",
                "slug": "39"
            },
            {
                "id": 43454,
                "tipo": 76,
                "tipo_slug": "talla",
                "tipo_nombre": "Talla",
                "nombre": "40",
                "slug": "40"
            },
            {
                "id": 23234,
                "tipo": 15,
                "tipo_slug": "color",
                "tipo_nombre": "Color",
                "nombre": "Rojo",
                "slug": "rojo"
            },
            {
                "id": 12408,
                "tipo": 15,
                "tipo_slug": "color",
                "tipo_nombre": "Color",
                "nombre": "Verde",
                "slug": "verde"
            }
        ]
    }

所需结果:

    {
        "id": 44906,
        "codigo": "DE0058751",
        "atributos": [
            {
                "id": 76,
                "slug": "talla",
                "nombre": "Talla",
                "atributos": [
                    {
                        "id": 15107,
                        "tipo": 76,
                        "nombre": "39",
                        "slug": "39"
                    },
                    {
                        "id": 12408,
                        "tipo": 76,
                        "nombre": "40",
                        "slug": "40"
                    }
                ]
            },
            {
                "id": 15,
                "slug": "color",
                "nombre": "Color",
                "atributos": [
                    {
                        "id": 34234,
                        "tipo": 15,
                        "nombre": "Rojo",
                        "slug": "rojo"
                    },
                    {
                        "id": 2323,
                        "tipo": 15,
                        "nombre": "Verde",
                        "slug": "verde"
                    }
                ]
            }                    
        ]
    }

问题是您丢失了文章的上下文。 我尝试了itertools groupby,但我认为DRF必须有另一个逻辑。

1 个答案:

答案 0 :(得分:1)

您可以重新定义to_representation中的ListSerializer,并将ListSerializer用作ArticuloSerializer的字段:

class GroupedAttributosSerializer(serializers.ListSerializer):
    def to_representation(self, data):
        data = super().to_representation(data)

        tipo_slug_to_objects = defaultdict(list)
        for d in data:
            tipo_slug_to_objects[d["tipo_slug"]].append(d)

        result = []

        for tipo_slug, objects in tipo_slug_to_objects.items():
            result.append(
                {
                    "id": objects[0]["tipo"],
                    "slug": tipo_slug,
                    "nombre": objects[0]["tipo_nombre"],
                    "attributos": [
                        {"id": attribute["id"],
                        "tipo": attribute["tipo"],
                        "nombre": attribute["nombre"],
                        "slug": attribute["slug"]}
                        for attribute in objects
                    ]
                }
            )
        return result

class ArticuloSerializer(serializers.ModelSerializer):
    atributos = GroupedAttributosSerializer(child=AtributoSerializer(), read_only=True, default=[])

    class Meta:
        model = Articulo
        fields = ["__all__"]