【问题标题】:How to speed up tastypie's queries with ToManyField如何使用 ToManyField 加速tastepie 的查询
【发布时间】:2016-01-17 17:34:30
【问题描述】:

在 resources.py 我有:

class CategoryResource(ModelResource):
    items = fields.ToManyField('ItemResource', 'items', full=True, null=False, readonly=True, related_name='items')
    class Meta:
        queryset = Category.objects.all().order_by('id')
        include_resource_uri = False
        always_return_data = True
        resource_name = 'category'

共有 6 个类别大约 5000 个项目。当我发出“列表”api 请求,即“api/1.0/category”时,它会对数据库进行大约 5000 次查询。我该如何优化它?我知道 full=False,但它不适合我的需要。

统一更新: 我找到了引起这么多查询的原因。我在 ItemResource 中有 'categories' 关系,所以 tastepie 为每个项目生成一个选择查询。

class ItemResource(ModelResource):
    categories = fields.ToManyField(CategoryResource, 'categories', null=True, readonly=True)

    def dehydrate_categories(self, bundle):
        categories = Category.objects.filter(owner_id=bundle.request.user.id, items__item=bundle.obj)
        return [category.name for category in categories]

当我请求 CategoryResource 时,显然这是不必要的数据,有什么方法可以将其从查询中排除?

【问题讨论】:

    标签: django optimization tastypie


    【解决方案1】:

    试试这个:

    def get_object_list(self, request):
        return super(CategoryResource, self).get_object_list(request) \
            .prefetch_related('items', 'items__categories')
    

    不要使用select_related,因为它返回重复的行。

    prefetch_related 进行了一次查询,返回所有项目,Django ORM 将其匹配到正确的行。

    编辑

    更改dehydrate_categories

    def dehydrate_categories(self, bundle):
        return [category.name for category in bundle.obj.categories.all() if category.owner == bundle.request.user]
    

    【讨论】:

    • 显示ItemResource和模型
    • 编辑:所以我的格式搞砸了,所以我重新发布为另一个答案。 |如果仍然存在问题,则可能是 ItemResource 也有一个或多个相关字段。如果是这种情况,您可能可以执行以下操作: def get_object_list(self, request): return super(CategoryResource, self).get_object_list(request).prefetch_related('items__relatedfield1', 'items__relatedfield2') 请记住,任何查询集更改如select_relatedprefetch_related performed on ItemResource`不会影响CategoryResource上相关模型的查询。
    • bundle.obj.categories 应该是 bundle.obj.categories.all()
    【解决方案2】:

    如果仍有问题,可能是 ItemResource 也有一个或多个相关字段。如果是这种情况,您可能可以执行以下操作:

    def get_object_list(self, request):
        return super(CategoryResource, self).get_object_list(request).prefetch_related('items__relatedfield1', 'items__relatedfield2')
    

    请记住,对ItemResource 执行的任何查询集更改(例如select_relatedprefetch_related)都不会影响对CategoryResource 上相关模型的查询。

    更新: 试试这样的:

    class CategoryResource(ModelResource):
        items = fields.ToManyField('ItemResource', 'items', full=True, null=False, readonly=True, related_name='items')
    
        class Meta:
            queryset = Category.objects.all().order_by('id')
            include_resource_uri = False
            always_return_data = True
            resource_name = 'category'
    
        def get_object_list(self, request):
            return super(CategoryResource, self).get_object_list(request) \
                .prefetch_related('items', 'items__categories')
    
    class ItemResource(ModelResource):
        categories = fields.ToManyField(CategoryResource, 'categories', null=True, readonly=True)
    
        def dehydrate_categories(self, bundle):
            categories = items__item=bundle.obj.categories.all()
            return [
                category.name
                for category in categories
                if category.owner_id == bundle.request.user.id
            ]
    

    或者:

    class CategoryResource(ModelResource):
        items = fields.ToManyField('ItemResource', 'items', full=True, null=False, readonly=True, related_name='items')
    
        class Meta:
            queryset = Category.objects.all().order_by('id')
            include_resource_uri = False
            always_return_data = True
            resource_name = 'category'
    
        def get_object_list(self, request):
            return super(CategoryResource, self).get_object_list(request) \
                .prefetch_related(
                    'items',
                    Prefetch('items__categories', queryset=Category.objects.filter(owner_id=bundle.request.user.id))
                )
    
    class ItemResource(ModelResource):
        categories = fields.ToManyField(CategoryResource, 'categories', null=True, readonly=True)
    
        def get_object_list(self, request):
            return super(ItemResource, self).get_object_list(request) \
                .prefetch_related(
                    Prefetch('categories', queryset=Category.objects.filter(owner_id=bundle.request.user.id))
                )
    
        def dehydrate_categories(self, bundle):
            return [
                category.name
                for category in bundle.obj.categories.all()
            ]
    

    此外,您可以对 ItemResource.categories 使用 ListField 而不是 ToManyField,因为您正在手动处理脱水。

    【讨论】:

    • 我有“项目对象没有属性'类别'”错误
    • 改用 category_set。我从你提供的代码中得到了“item.categories”。
    猜你喜欢
    • 2014-03-21
    • 1970-01-01
    • 2014-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-30
    • 1970-01-01
    • 2020-08-21
    相关资源
    最近更新 更多