如何使用ToManyField加速tastypie的查询

时间:2022-02-18 21:45:21

In resources.py I have:

在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'

There are about 5000 items in 6 categories. When I am make 'list' api request i.e. 'api/1.0/category', it makes about 5000 queries to database. How can I optimize it? I know about full=False, but it isn't suit my needs.

6类中约有5000种。当我制作'list'api请求,即'api / 1.0 / category'时,它会对数据库进行大约5000次查询。我该如何优化它?我知道full = False,但它不适合我的需要。

UPD: I found what caused so much queries. I have 'categories' relation in ItemResource, so tastypie generate a select query for each item.

UPD:我发现了导致如此多查询的原因。我在ItemResource中有'类别'关系,因此tastypie为每个项生成一个选择查询。

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]

Obviously that is unnecessary data when I requesting a CategoryResource, is there any way to exclude it from queries?

当我请求CategoryResource时,显然这是不必要的数据,有没有办法将它从查询中排除?

2 个解决方案

#1


3  

Try this:

尝试这个:

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

Do not use select_related, because it return duplicate rows.

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

prefetch_related made one query, that returns all items and Django ORM match it to proper rows.

prefetch_related做了一个查询,返回所有项目,Django ORM将它与正确的行匹配。

EDIT

Change dehydrate_categories

更改dehydrate_categories

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

#2


2  

If there's still a problem, it could be that ItemResource also has one or more related fields. If that's the case, you can probably do something like:

如果仍有问题,可能是ItemResource还有一个或多个相关字段。如果是这种情况,您可以做以下事情:

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

Keep in mind, any queryset alterations such as select_related or prefetch_related performed on ItemResource won't affect the query for related models on CategoryResource.

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

UPDATE: Try something like this:

更新:尝试这样的事情:

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
        ]

OR:

要么:

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()
        ]

Also, you can probably use a ListField instead of ToManyField for ItemResource.categories since you're manually handling the dehydration.

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

#1


3  

Try this:

尝试这个:

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

Do not use select_related, because it return duplicate rows.

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

prefetch_related made one query, that returns all items and Django ORM match it to proper rows.

prefetch_related做了一个查询,返回所有项目,Django ORM将它与正确的行匹配。

EDIT

Change dehydrate_categories

更改dehydrate_categories

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

#2


2  

If there's still a problem, it could be that ItemResource also has one or more related fields. If that's the case, you can probably do something like:

如果仍有问题,可能是ItemResource还有一个或多个相关字段。如果是这种情况,您可以做以下事情:

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

Keep in mind, any queryset alterations such as select_related or prefetch_related performed on ItemResource won't affect the query for related models on CategoryResource.

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

UPDATE: Try something like this:

更新:尝试这样的事情:

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
        ]

OR:

要么:

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()
        ]

Also, you can probably use a ListField instead of ToManyField for ItemResource.categories since you're manually handling the dehydration.

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