如何使用 ToManyField 加速 tastypie 的查询
How to speed up tastypie's queries with ToManyField
在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 个项目。当我发出 'list' api 请求时,即 'api/1.0/category',它对数据库进行了大约 5000 次查询。我该如何优化它?我知道 full=False,但它不适合我的需要。
更新:
我发现是什么导致了如此多的查询。我在 ItemResource 中有 'categories' 关系,所以 tastypie 为每个项目生成一个 select 查询。
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 时,这是不必要的数据,有什么办法可以从查询中排除它吗?
试试这个:
def get_object_list(self, request):
return super(CategoryResource, self).get_object_list(request) \
.prefetch_related('items', 'items__categories')
不要使用 select_related
,因为它 return 重复行。
prefetch_related
进行了一次查询,return 所有项目和 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也有一个或多个相关字段。如果是这种情况,您可能可以执行以下操作:
def get_object_list(self, request):
return super(CategoryResource, self).get_object_list(request).prefetch_related('items__relatedfield1', 'items__relatedfield2')
请记住,在 ItemResource
上执行的任何查询集更改(例如 select_related
或 prefetch_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()
]
另外,您可以使用 ListField 而不是 ToManyField ItemResource.categories,因为您是手动处理脱水。
在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 个项目。当我发出 'list' api 请求时,即 'api/1.0/category',它对数据库进行了大约 5000 次查询。我该如何优化它?我知道 full=False,但它不适合我的需要。
更新: 我发现是什么导致了如此多的查询。我在 ItemResource 中有 'categories' 关系,所以 tastypie 为每个项目生成一个 select 查询。
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 时,这是不必要的数据,有什么办法可以从查询中排除它吗?
试试这个:
def get_object_list(self, request):
return super(CategoryResource, self).get_object_list(request) \
.prefetch_related('items', 'items__categories')
不要使用 select_related
,因为它 return 重复行。
prefetch_related
进行了一次查询,return 所有项目和 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也有一个或多个相关字段。如果是这种情况,您可能可以执行以下操作:
def get_object_list(self, request):
return super(CategoryResource, self).get_object_list(request).prefetch_related('items__relatedfield1', 'items__relatedfield2')
请记住,在 ItemResource
上执行的任何查询集更改(例如 select_related
或 prefetch_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()
]
另外,您可以使用 ListField 而不是 ToManyField ItemResource.categories,因为您是手动处理脱水。