Сериализация вложенных объектов в DRF - PullRequest
2 голосов
/ 17 января 2020

У меня на сайте есть тестовый объект, который я пытаюсь правильно ввести в базу данных. Это объект заказа с вложенными данными о продукте. Вот пример JSON, отправляемого внешним интерфейсом:

{
    "phone": "123456789",
    "first_name": "name",
    "delivery_date": "2020-01-06",
    "delivery_time": 2,
    "address": "address",
    "comment": "comment",
    "payment": 0,
    "order_items": [
      {
        "quantity": 2,
        "pizza": 1
      },
      {
        "quantity": 3,
        "pizza": 2
      }
    ]
}

Вот мой serializers.py

class OrderItemSerializer(serializers.ModelSerializer):
    quantity = serializers.IntegerField()
    pizza = serializers.SerializerMethodField()
    print(pizza, quantity)

    def get_pizza(self, obj):
        print(obj.pizza.id)
        return obj.pizza.id

    class Meta:
        model = OrderItem
        fields = ('quantity',
                  'pizza',)


class OrderSerializer(serializers.ModelSerializer):
    order_items = serializers.SerializerMethodField()

    """
    Calculate order_items field 
    """
    def get_order_items(self, obj):
        items = obj.orderitem_set.all()
        print(obj)
        print(items)
        return OrderItemSerializer(items, many=True).data

    class Meta:
        model = Order
        fields = ('phone',
                  'first_name',
                  'delivery_date',
                  'delivery_time',
                  'address',
                  'comment',
                  'payment',
                  'order_items',)

В результате order_items всегда пуст: "order_items": []

Я использую Swagger для тестирования и документирования API. Я могу отправлять заказы напрямую оттуда без ошибок, но даже когда я добавляю элементы заказа вручную, они все равно не отображаются в теле ответа сервера. Это должно означать, что я не обрабатываю вложенные объекты должным образом.

POST

{
    "phone": "123456",
    "first_name": "string",
    "delivery_date": "2000-10-21",
    "delivery_time": 2,
    "address": "string",
    "comment": "string",
    "payment": 0,
    "order_items": [
        {"pizza": 2,"quantity": 3},
        {"pizza": 1,"quantity": 4}]
}

201, тело ответа:

{
  "phone": "123456",
  "first_name": "string",
  "delivery_date": "2000-10-21",
  "delivery_time": 2,
  "address": "string",
  "comment": "string",
  "payment": 0,
  "order_items": []
}

Я также настроил некоторые print строк в коде выше, и похоже, что OrderItemSerializer вообще не используется. get_order_items печатает идентификатор заказа и пустой массив, в то время как get_pizza ничего не печатает при оформлении заказа на сайте.

Ответы [ 2 ]

2 голосов
/ 17 января 2020

Вам необходимо переопределить метод create() сериализатора, чтобы сделать записываемые вложенные сериализаторы .

Попробуйте что-то вроде этого:

class OrderSerializer(serializers.ModelSerializer):
    order_items = serializers.SerializerMethodField()

    """
    Calculate order_items field 
    """
    def get_order_items(self, obj):
        items = obj.orderitem_set.all()
        print(obj)
        print(items)
        return OrderItemSerializer(items, many=True).data

    class Meta:
        model = Order
        fields = ('phone',
                  'first_name',
                  'delivery_date',
                  'delivery_time',
                  'address',
                  'comment',
                  'payment',
                  'order_items',)

    def create(self, validated_data):
        items_data = validated_data.pop('order_items')
        order = Order.objects.create(**validated_data)
        for item_data in items_data:
            OrderItem.objects.create(order=order, **item_data)
        return order

Также вы можете просто использовать OrderItemSerializer как поле без SerializerMethodField. Вы можете использовать source аргумент для указания поля source :

order_items = serializers.OrderItemSerializer(many=True, source="orderitem_set")
0 голосов
/ 25 января 2020

В конце концов я придумал это. Мне пришлось добавить переменную order_items к Order объекту, который никоим образом не используется и просто тратит пространство в БД. Несмотря на то, что это не большая трата, я не думаю, что это хороший способ написать код в любом случае.

Если кто-то может предложить лучшую версию в комментариях или другом ответе, я попробую это как хорошо.

from rest_framework import serializers
from .models import Order, OrderItem
import copy


class OrderItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = OrderItem
        fields = ('quantity',
                  'pizza',)


class OrderSerializer(serializers.ModelSerializer):
    order_items = OrderItemSerializer(many=True)

    class Meta:
        model = Order
        fields = ('phone',
                  'first_name',
                  'delivery_date',
                  'delivery_time',
                  'address',
                  'comment',
                  'payment',
                  'order_items')

    def create(self, validated_data, **kwargs):
        for_items = copy.deepcopy(validated_data)

        # create order object
        del validated_data['order_items']
        print('VALID', validated_data)
        order = Order.objects.create(**validated_data)

        # create order items
        for item in for_items.pop('order_items'):
            order_item = dict(item.items())
            print('ITEM', order_item)
            OrderItem.objects.create(order=order, **order_item)

        return order
...