Работа над проектом электронной коммерции. Проект имеет родственные модели. Категория модели имеет наследование MPTT. Он использует Django Rest Framework для связи между API. Недавно иностранная служба попросила меня поместить полный путь к категории в ответ XML на моей стороне. Но этот запрос вызвал очень высокие запросы в БД. Мне нужно уменьшить количество запросов, но я не могу понять, как это сделать в рамках сериализации DRF. Я попробовал несколько способов. Мой последний подход ниже с представлением модели и сериализацией.
class Category(MPTTModel):
parent = TreeForeignKey('self', blank=True, null=True, related_name='children')
root = TreeForeignKey('self', blank=True, null=True, related_name='leaf')
name = models.CharField(max_length=100)
class ProductMeta(models.Model):
...
category = models.ForeignKey('Category', null=True, blank=True, db_index=True, related_name='category')
...
class Product(models.Model):
...
meta = models.ForeignKey(ProductMeta, related_name='product')
...
А некоторые DRF-представления визуализируют данные модели в XML
class ProductMetaBaseViewSet(viewsets.ModelViewSet):
def get_serializer_class(self):
return ProductMetaSerializer
def get_queryset(self):
queryset = ProductMeta.objects.all().prefetch_related('products', 'category__root')
return self.paginate_queryset(queryset)
def list(self, request):
serializer = ProductMetaSerializer(self.get_queryset(), many=True)
return Response(serializer.data)
class ProductMetaXMLViewSet(ProductMetaBaseViewSet, viewsets.ModelViewSet):
parser_classes = (XMLParser,)
renderer_classes = (XMLRenderer,)
А вот сериализаторы для получения данных:
class RootCategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('id', 'name')
class CategorySerializer(serializers.ModelSerializer):
root = RootCategorySerializer()
full_category_path = serializers.SerializerMethodField()
class Meta:
model = Category
fields = ('name', 'root', 'category_path')
def get_full_category_path(self, obj):
related_ancestor_name_list = []
related_ancestor_list = []
next_rel_name = ""
next_rel = None
cat_level = obj.get_level()
for i in range(cat_level):
if i <= 0 and not next_rel_name:
next_rel_name = 'name'
next_rel = "parent"
else:
next_rel_name = "{}__{}".format("parent", next_rel_name)
next_rel = "{}__parent".format(next_rel)
related_ancestor_name_list.append(next_rel_name)
if next_rel is not None:
related_ancestor_list.append(next_rel)
print(related_ancestor_name_list, related_ancestor_list)
cobj = Category.objects.filter(pk=obj.pk).select_related(*related_ancestor_list).prefetch_related(*related_ancestor_list).values_list(*related_ancestor_name_list[::-1]).first()
return ' > '.join(cobj)
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('price', 'stock')
class ProductMetaSerializer(serializers.ModelSerializer):
products = ProductSerializer(many=True, read_only=True)
category = CategorySerializer(read_only=True)
class Meta:
model = ProductMeta
fields = ('name', 'category', 'products')
В моей тестовой базе данных, если я не использую метод get_full_category_path, в логгер записывается 20 запросов. Когда мне нужно использовать этот метод для извлечения полных путей к категориям, количество запросов возрастает до 100.
Я опубликовал свою последнюю попытку. Я также пытался использовать метод get_ancestors MPTT, но это не влияет на количество попаданий в БД. В любом случае, при сериализации каждого объекта продукта выдается очень большое количество запросов модели категорий.
PS: я знаю, что лучшим вариантом является кэширование дерева, но мне действительно интересно, есть ли способ уменьшить попадания в БД при выполнении рекурсивных запросов MPTT.