带直通字段的嵌套M2M序列化器

时间:2019-04-22 23:45:30

标签: django django-rest-framework django-serializer

我的模特:

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": []
        }
      ]
    }
  ]
},

基本上,我想获得item的嵌套(最多4个)及其通过模型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。我认为应该发生的是,ItemSerializerRecipeSerializer内部用source='item'简单地调用。但是,这是不可能的,因为RecipeSerializer内部已经调用了ItemSerializer,这意味着必须使用字符串名称(例如类似serializers.GetSerializer('apps.myapp.serializers.ItemSerializer')(source='item')的字符串)来调用它

解决这个问题的正确方法是什么?

编辑: 这些值或多或少是固定的,并且永远不会超过嵌套深度3/4。

编辑: 我使用它来工作:

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个查询。预取对于使查询高效至关重要,因此解决方案必须利用预取的值。

0 个答案:

没有答案