Мои модели:
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 запросов, даже если данные предварительно выбраны. Предварительная выборка очень важна для обеспечения эффективности запроса, поэтому в решении должны использоваться предварительно выбранные значения.