从事电子商务项目。项目有相关模型。类别模型具有 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 个 ish 查询记录到记录器中。当我需要使用该方法来获取完整类别路径查询时,查询会上升到 100 多个。
我发布了我最近的尝试。我也尝试使用 MPTT 的 get_ancestors 方法,但它不受数据库命中计数的影响。无论哪种方式,对每个产品对象序列化都会产生非常大量的类别模型查询。
PS:我知道最好的选择是缓存树,但我真的想知道在进行递归 MPTT 查询时是否有办法减少 DB 命中。