Как я могу уменьшить рекурсивные запросы дерева MPTT при сериализации с DRF - PullRequest
2 голосов
/ 29 апреля 2019

Работа над проектом электронной коммерции. Проект имеет родственные модели. Категория модели имеет наследование 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.

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