Обновление нескольких вложенных объектов в DRF

Вопрос или проблема

У меня есть три связанные модели с отношением “многие ко многим”.

У меня нет идеи, как обновить все экземпляры сразу.

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

class PlannedChargePriceSerializer(serializers.ModelSerializer):
    class Meta:
        model = ChargePrice
        fields = ['id', 'start', 'end', 'price']

class PlannedChargeSerializer(serializers.ModelSerializer):
    prices = PlannedChargePriceSerializer(many=True)

    class Meta:
        model = Charge
        fields = ['id', 'type', 'qtd', 'status', 'prices']

class PlannedSerializer(serializers.ModelSerializer):
    charges = PlannedChargeSerializer(many=True)

    class Meta:
        model = Planned
        fields="__all__"
        read_only_fields = ['id', 'date', 'charges']

    def create(self, validated_data):
        charges_data = validated_data.pop('charges')
        planned = Planned.objects.create(**validated_data)

        for charge_data in charges_data:
            charge_prices_data = charge_data.pop('prices')
            charge = Charge.objects.create(planned=planned, **charge_data)

            for charge_price_data in charge_prices_data:
                ChargePrice.objects.create(charge=charge, **charge_price_data)

        return planned

    def update(self, instance, validated_data):
        # Никакой идеи, как обновить несколько вложенных объектов
        return instance

Ответ или решение

Для обновления нескольких связанных объектов в Django REST Framework (DRF) с использованием вложенных сериализаторов, нам необходимо не только реализовать логику обновления для базового объекта, но и корректно обработать изменения связанных объектов (в вашем случае — это объекты Charge и ChargePrice).

Разберем обновление, шаг за шагом, используя ваш код в качестве основы.

Обновление вложенных объектов

  1. Получение вложенных данных: Извлекаем данные из validated_data.
  2. Обновление существующих объектов: Для каждой связи проверяем, существует ли объект и обновляем его, иначе создаем новый.
  3. Обработка списка цен: Обновляем или создаем связанные объекты ChargePrice.

Вот как будет выглядеть метод update:

def update(self, instance, validated_data):
    charges_data = validated_data.pop('charges', [])

    # Обновляем основные поля объекта Planned
    instance.some_field = validated_data.get('some_field', instance.some_field)  # Пример обновления поля
    instance.save()

    # Сохраняем обновленные или создаем новые Charge
    existing_charges_ids = [charge.id for charge in instance.charges.all()]
    new_charge_ids = []

    for charge_data in charges_data:
        charge_id = charge_data.get('id', None)
        prices_data = charge_data.pop('prices', [])

        if charge_id in existing_charges_ids:
            # Если Charge уже существует, обновляем его
            charge = Charge.objects.get(id=charge_id, planned=instance)
            charge.type = charge_data.get('type', charge.type)
            charge.qtd = charge_data.get('qtd', charge.qtd)
            charge.status = charge_data.get('status', charge.status)
            charge.save()
        else:
            # Если Charge не существует, создаем его
            charge = Charge.objects.create(planned=instance, **charge_data)

        new_charge_ids.append(charge.id)

        # Сохраняем или обновляем ChargePrice
        existing_prices_ids = [price.id for price in charge.prices.all()]
        for price_data in prices_data:
            price_id = price_data.get('id', None)

            if price_id in existing_prices_ids:
                # Обновляем существующую цену
                price = ChargePrice.objects.get(id=price_id, charge=charge)
                price.start = price_data.get('start', price.start)
                price.end = price_data.get('end', price.end)
                price.price = price_data.get('price', price.price)
                price.save()
            else:
                # Создаем новую цену
                ChargePrice.objects.create(charge=charge, **price_data)

    # Удаляем Charge, если они не включены в запрос
    for charge in instance.charges.exclude(id__in=new_charge_ids):
        charge.delete()

    return instance

Пояснения к коду:

  • Обновление основного объекта: Извлекаем данные validated_data и обновляем необходимые поля основного объекта Planned.
  • Работа с объектами Charge: Проверяем, существуют ли переданные объекты Charge. Если они существуют, обновляем их. Если нет — создаем новые.
  • Работа с объектами ChargePrice: Аналогично обновляем существующие цены и создаем новые по мере необходимости.
  • Удаление старых объектов: Если какие-то объекты Charge больше не указаны в данных запроса, мы можем удалить их.

Этот подход сохраняет целостность данных и управляет обновлением вложенных объектов в соответствии с вашими требованиями в рамках DRF. Надеюсь, это поможет вам эффективно обновлять ваши вложенные объекты!

Оцените материал
Добавить комментарий

Капча загружается...