1

我有一个简单的模型,其中包括一个产品和类别表。Product 模型有一个外键 Category。

当我进行一个返回类别列表 /api/vi/categories/ 的美味派 API 调用时,我想添加一个字段来确定“产品计数”/具有给定类别的产品数量。结果将是这样的:

category_objects[
{ 
   id: 53
   name: Laptops
   product_count: 7
}, 
...
 ]

以下代码正在运行,但对我的数据库的影响很大

    def dehydrate(self, bundle):
        category = Category.objects.get(pk=bundle.obj.id)
        products = Product.objects.filter(category=category)
        bundle.data['product_count'] = products.count()
        return bundle  

有没有更有效的方法来构建这个查询?也许用注释?

4

2 回答 2

4

您可以使用prefetch_related方法QuerSet来反转 select_related。

Asper 文档,

prefetch_related(*查找)

返回一个 QuerySet,它将在单个批次中自动检索每个指定查找的相关对象。

这与 select_related 具有相似的目的,因为两者都旨在阻止由访问相关对象引起的大量数据库查询,但策略完全不同。

如果您将脱水功能更改为以下功能,则数据库将被单次命中。

def dehydrate(self, bundle):
    category = Category.objects.prefetch_related("product_set").get(pk=bundle.obj.id)
    bundle.data['product_count'] = category.product_set.count()
    return bundle 

更新 1

您不应该在脱水函数中初始化查询集。查询集应始终Meta仅在类中设置。请查看django-tastypie文档中的以下示例。

class MyResource(ModelResource):
    class Meta:
        queryset = User.objects.all()
        excludes = ['email', 'password', 'is_staff', 'is_superuser']

    def dehydrate(self, bundle):
        # If they're requesting their own record, add in their email address.
        if bundle.request.user.pk == bundle.obj.pk:
            # Note that there isn't an ``email`` field on the ``Resource``.
            # By this time, it doesn't matter, as the built data will no
            # longer be checked against the fields on the ``Resource``.
            bundle.data['email'] = bundle.obj.email

        return bundle

根据有关功能的官方django-tastypie 文档,dehydrate()

脱水

dehydrate 方法采用现在已完全填充的 bundle.data 并对它进行任何最后的更改。当一条数据可能依赖多个字段时,如果您想插入不值得拥有自己的字段的额外数据,或者如果您想从要返回的数据中动态删除内容,这很有用。

dehydrate()仅用于对 bundle.data 进行任何最后更改。

于 2013-01-12T08:05:11.120 回答
2

您的代码对每个类别进行额外的计数查询。您对annotate此类问题的帮助是正确的。

Django 将在GROUP BY语句中包含所有查询集的字段。注意.values()并将.group_by()服务限制字段设置为必填字段。

cat_to_prod_count = dict(Product.objects
                                .values('category_id')
                                .order_by()
                                .annotate(product_count=Count('id'))
                                .values_list('category_id', 'product_count'))

上面的dict对象是一个地图 [category_id -> product_count]。

它可以在dehydrate方法中使用:

 bundle.data['product_count'] = cat_to_prod_count[bundle.obj.id]

如果这没有帮助,请尝试在类别记录上保留类似的计数器并使用信号使其保持最新。

注意类别通常是树状的存在,您可能还想计算所有子类别。

在这种情况下,请查看包django-mptt

于 2013-01-12T08:09:30.230 回答