Вложенный сериализатор М2М со сквозным полем - PullRequest
0 голосов
/ 23 апреля 2019

Мои модели:

class Item(models.Model):
    wiki_url = models.URLField(_('wiki url'))
    img_url = models.URLField(_('img url'))
    name = models.CharField(_('name'), max_length=255, unique=True)
    item_id = models.PositiveIntegerField(_('item id'), unique=True, db_index=True) # refers to ingame_id

    child_items = models.ManyToManyField(
        'Item',
        through='Recipe',
    )

    def __str__(self):
        return f'{self.name}'

class Recipe(models.Model):
    parent_item = models.ForeignKey(Item, on_delete=models.CASCADE)
    item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='child_item')
    quantity = models.PositiveSmallIntegerField(_('quantity'))

    class Meta:
        unique_together = ['parent_item', 'item']

    def __str__(self):
        return f'{self.quantity}x {self.item}'

Что я хочу получить:

{
  "item_id": 5265,
  "name": "A",
  "wiki_url": "some_url",
  "img_url": "some_url",
  "children": [
    {
      "item_name": "B",
      "item_id": 5194,
      "wiki_url": "some_url",
      "img_url": "some_url",
      "quantity": 248,
      "children": []
    },
    {
      "item_name": "C",
      "item_id": 3950,
      "wiki_url": "some_url",
      "img_url": "some_url",
      "quantity": 1000,
      "children": [
        {
          "item_name": "D",
          "item_id": 5194,
          "wiki_url": "some_url",
          "img_url": "some_url",
          "quantity": 248,
          "children": []
        }
      ]
    }
  ]
},

В основном я хочу получить вложенность (до 4) item, представленную вместе со значением quantity в сквозной модели Recipe.

На данный момент мои сериализаторы выглядят так:

class RecipeSerializer(ModelSerializer):
    item_name = ReadOnlyField(source='item.name')
    item_id = ReadOnlyField(source='item.item_id')
    wiki_url = ReadOnlyField(source='item.wiki_url')
    img_url = ReadOnlyField(source='item.img_url')

    class Meta:
        model = Recipe
        fields = ('item_name', 'item_id', 'wiki_url', 'img_url', 'quantity')


class ItemSerializer(ModelSerializer):
    children = RecipeSerializer(source='recipe_set', many=True)

    class Meta:
        model = Item
        fields = ('item_id', 'name', 'wiki_url', 'img_url', 'children')

На данный момент существует только один уровень вложенности, то есть children не имеет последующих children. Я думаю, что должно произойти, это то, что ItemSerializer просто вызывается внутри RecipeSerializer с source='item'. Однако это невозможно, так как RecipeSerializer уже вызывается внутри ItemSerializer, что означает, что он должен вызываться с именем строки (например, что-то вроде serializers.GetSerializer('apps.myapp.serializers.ItemSerializer')(source='item'))

Как правильно это сделать?

Edit: Значения более или менее фиксированы и никогда не будут превышать глубину 3/4 вложенности.

Edit: Я получил его на работу с помощью:

class RecursiveSerializer(Serializer):
    def to_representation(self, instance):
        data = dict(ItemSerializer(instance=instance.item, context=self.context).data)
        data.update({'quantity': instance.quantity})
        return data


class ItemSerializer(ModelSerializer):
    children = RecursiveSerializer(many=True, read_only=True, source='recipe_set')

    class Meta:
        model = Item
        fields = ('item_id', 'name', 'wiki_url', 'img_url', 'children')

Однако теперь проблема в том, что этот метод не использует prefetched элементов, то есть существует n + 1 запросов, даже если данные предварительно выбраны. Предварительная выборка очень важна для обеспечения эффективности запроса, поэтому в решении должны использоваться предварительно выбранные значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...