【问题标题】:Tastypie Dehydrate reverse relation countTastypie Dehydrate 反向关系计数
【发布时间】:2013-01-11 20:49:29
【问题描述】:

我有一个简单的模型,其中包括产品和类别表。 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  

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

【问题讨论】:

    标签: python django tastypie


    【解决方案1】:

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

    Asper 文档,

    prefetch_related(*查找)

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

    这与 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-tastypiedocumentationdehydrate()功能,

    脱水

    脱水方法采用现在已完全填充的 bundle.data & make 对它的任何最后改动。这对于当一条数据 可能取决于多个领域,如果你想多加一点 不值得拥有自己的领域的数据,或者如果您想要 从要返回的数据中动态删除内容。

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

    【讨论】:

    • 这不是真的。 .count() 调用执行单个数据库查询。问题是每个类别都调用了dehydrate 函数。您的代码实际上做了两个查询。一个用于prefetch_related,另一个用于.count() 调用,它根本不使用以前的数据库查询。相反,它执行单独的SELECT COUNT(*) FROM ... 查询。
    • 谢谢 - 在深入研究美味的文档之后,我应该将查询集设置在脱水之外
    【解决方案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。

    【讨论】:

    • 此解决方案将查询时间减少了一半。如果存在 0/没有产品关系,我确实必须在我的代码中添加一个 Try except 来处理
    猜你喜欢
    • 2012-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-22
    • 2013-12-10
    • 1970-01-01
    • 1970-01-01
    • 2018-07-05
    相关资源
    最近更新 更多