我目前正在开发一个Django库来管理多个分辨率的图像(django-multires),我无法优化保留关系查询。在我解释这个问题之前,让我试着解释一下我想要实现的目标。
背景
这个想法是存储图像的多个分辨率,但是根据图像路径而不是外键来保持对原始图像的引用。我认为一个例子会更有意义:
# the goal is to store multiple resolutions for 'image' field
class FooModel(models.Model):
image = MultiresImageField(...)
# MultiresImage.source will be the identical to FooModel.image
# so MultiresImage.source will act sort of like a foreign key
class MultiresImage(models.Model):
source = models.ImageField(...)
...
使用此方法而不是使用外键(或通用外键)链接到源图像允许向源模型添加多个MultiresImageField
:
class FooModel(models.Model):
image = MultiresImageField(...)
image2 = MultiresImageField(...)
现在假设您需要为源模型中的图像字段获取所有不同的分辨率:
foo = FooModel(...)
foo.image.get_all_multires_images()
# which behind the scenes will do something similar to
return MultiresImage.objects.filter(source=foo.image.name)
这很有效,直到你需要查询多个FooModel
实例,在这种情况下,对于每个模型实例,我被迫进行数据库查找以获得该模型的所有分辨率:
sources = FooModel.objects.filter(...)
for source in sources:
# this incurs a db query
foo.image.get_all_multires_images()
通常我会使用prefetch_related
来进行性能优化,但是在这里我不能,因为我的多重模型没有源模型的外键,因此源模型上不存在反向关系。 / p>
问题
所以我的问题是如何优化上述查询?
目前的一些想法
contribute_to_class
手动向源模型添加反向关系,但我无法弄清楚如何?Prefetch
API,但我也无法弄清楚如何做到这一点。答案 0 :(得分:0)
最简单但可能不是最优雅的解决方案是编写一种辅助类:
def prefetch_related_images(image_queryset):
multires_images = MultiresImage.objects.filter(source__in=[image.name for image in image_queryset ])
for image in image_queryset:
image.multires_images = []
for multires_image in multires_images:
if multires_image.source == image.name:
image.multires_images.append(multires_image)
而且,一个更优雅的解决方案可能与您对contrib_to_class的想法一致。你为什么不尝试像通用关系这样的东西:
class MultiresImage(models.Model):
source = models.ImageField(...)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
image_target= GenericForeignKey('content_type', 'object_id')
然后像那样修改contrib_to_class:
def contribute_to_class(self, cls, name):
"""
Attached necessary garbage collection signals.
"""
super(MultiresImageField, self).contribute_to_class(cls, name)
setattr(cls,'%_source', GenericRelation(MultiresImage)
然后处理通过信号管理关系的细节(就像你已经做的那样)。