Вложенная сериализация ManytoMany в Django Rest Framework - PullRequest
0 голосов
/ 04 августа 2020

В моем приложении у меня есть вложенное отношение многие ко многим, например следующее:

Models.py

class ReturnKitsProducts(models.Model):

    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.IntegerField(default=0)


class ReturnKits(models.Model):

    kit = models.ForeignKey(Kit, on_delete=models.CASCADE)
    quantity = models.IntegerField(default=0)
    items = models.ManyToManyField(ReturnKitsProducts)


class Return(models.Model):

    transaction_date = models.DateTimeField(default=datetime.now)
    transaction_no = models.IntegerField(default=0, blank=True, null=True)
    flow = models.ForeignKey(Flow, on_delete=models.CASCADE)
    kits = models.ManyToManyField(ReturnKits)
    warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE)

В этом ReturnKitsProducts подключен к ReturnKits как M2M и ReturnKits подключен к Return как M2M. У меня есть только один уровень сериализации M2M для обновления и создания, например:

Serializers.py

class ReturnKitsSerializer(serializers.ModelSerializer):

    class Meta:
        model = ReturnKits
        fields = "__all__"

class ReturnSerializer(serializers.ModelSerializer):

    kits = ReturnKitsSerializer(many=True)

    class Meta:
        model = Return
        fields = "__all__"


    def create(self, validated_data):
        items_objects = validated_data.pop('kits', None)
        prdcts = []
        for item in items_objects:
            i = ReturnKits.objects.create(**item)
            prdcts.append(i)
        instance = Return.objects.create(**validated_data)
        print("prdcts", prdcts)
        instance.items.set(prdcts)
        return instance

Но я не уверен, как выполнить сериализацию в вышеупомянутом сценарии. Пожалуйста, помогите !!

Ответы [ 2 ]

0 голосов
/ 04 августа 2020

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

Если вы не выполните следующие действия, вы пропустите все функции, которые предоставляет класс сериализатора и, возможно, потребуются написать весь свой код самостоятельно в функции create вашего основного сериализатора, который будет грязным.

class ItemSerializer(serializers.ModelSerializer):

    class Meta:
        model = ReturnKitsProducts
        fields = "__all__"

class ReturnKitsSerializer(serializers.ModelSerializer):
    items = ItemSerializer(many=True)  # same as 

    class Meta:
        model = ReturnKits
        fields = "__all__"

     def validate_items(self, value):
        serializer = ItemSerializer(data=value, many=True)
        if serializer.is_valid():
            return serializer # this will be finally received and used in create function of main serializer
        else:
            raise serializers.ValidationError(serializer.errors)

    def create(self, validated_data):

        items = validated_data.pop('items')
        r = ReturnKits.objects.create(**validated_data) 
        for item in items:
            r.items.add(item.save()) # save each serializer (already validated)  and append saved object

        return r

class ReturnSerializer(serializers.ModelSerializer):
    kits = ReturnKitsSerializer(many=True)
     
    def validate_kits(self, value):
        serializer = ReturnKitsSerializer(data=value, many=True)
        if serializer.is_valid():
            return serializer
        else:
            raise serializers.ValidationError(serializer.errors)

    class Meta:
        model = Return
        fields = "__all__"

    def create(self, validated_data):

        # IMP: At this point all the data (including nested) will have been validated, so no error will throw when saving the data

        kits = validated_data.pop('kits', None)
        instance = Return.objects.create(**validated_data)
        for kit in kits:
            i = kits.save() # save each serializer (already validated)  and get a object
            instance.kits.add(i)
        return instance

Этот код будет работать для create и get. Если вы хотите обновить все свои данные сразу, вам следует разработать подход.

Вы можете попробовать подход, который я использую:

Обновление данных с их вложенными данными

  • Если словарь содержит поле id, соответствующие вложенные данные будут обновлены.

  • Если словарь не содержит поля id, новые вложенные данные будут создано.

  • Если словарь содержит только id в качестве ключа, вложенные данные будут удалены.

  • Нет необходимости предоставлять вложенные данные, которые не нужно обновлять или удалять.

https://github.com/SagarKAdhikari/drf-nested-relations: библиотека, в которой вы можете проверять и сохранять / обновлять вложенные данные в любая глубина , хотя она работает только для общих c отношений и внешних ключей (поскольку в моем проекте требовалось только это). Вы можете попробовать понять код и имплементацию, если ваши вложенные отношения намного глубже, чем в настоящее время, и у вас их слишком много.

0 голосов
/ 04 августа 2020

Вы можете попробовать что-то вроде этого:

class ItemSerializer(serializers.ModelSerializer):

    class Meta:
        model = ReturnKitsProducts
        fields = "__all__"

class ReturnKitsSerializer(serializers.ModelSerializer):
    items = ItemSerializer(many=True)  # same as 

    class Meta:
        model = ReturnKits
        fields = "__all__"

class ReturnSerializer(serializers.ModelSerializer):
    kits = ReturnKitsSerializer(many=True)

    class Meta:
        model = Return
        fields = "__all__"


    def create(self, validated_data):
        items_objects = validated_data.pop('kits', None)
        instance = Return.objects.create(**validated_data)
        for item in item_objects:
            return_products = item.pop('items')
            i = ReturnKits.objects.create(**item)
            for data in return_products:
               return_product = ReturnKitsProducts.objects.create(**data)
               i.items.add(return_product)
            instance.items.add(i)
        return instance

Я вытащил данные из словаря validated_data и при необходимости создал экземпляры.

...