我有一个数据库结构,可以简化如下(版本和附加信息未显示,因为与我的问题没有直接关系):
class Image(models.Model):
file = models.ImageField(blank=False, null=False, upload_to='images')
version = models.ForeignKey(Version, blank=False, null=False)
class ExampleImage(models.Model):
example = models.ForeignKey('Example', blank=False, null=False)
image = models.ForeignKey(Image, blank=False, null=False)
additional_info = models.ForeignKey(AdditionalInfo, blank=True, null=True)
class Example(models.Model):
name = models.Charfield(max_length=255)
images = models.ManyToManyField(Image, through=ExampleImage, through_fields=('example', 'image')
def get_default_image(self):
try:
image = self.images.get(version=Version.objects.get(name=DEFAULT_VERSION))
except Image.DoesNotExist:
# Get some other image, this happens maybe 1 out of 10
现在我非常需要查询所有Example对象及其默认图像。目前,我有点喜欢以下内容:
example_list = []
examples = Example.objects.all()
for example in examples:
example_dict = dict(name=example.name, image=example.get_default_image())
example_list.append(example_dict)
# Then show example_list in template
这种方法有效但会导致成千上万的数据库查询,并且需要花费一分多钟来执行,这对于下载单个网页来说太长了!
所以我的问题是,优化这种用例的正确方法是什么。我可以为Example模型设置default_image字段(然后使用select_related,但这会使图像更复杂),将版本ID硬编码为get_default_image方法(不那么灵活),将get_default_image方法设置为cached_property等但是我&#39 ;我不确定应该使用哪种方法。我已经尝试了很多技巧,但似乎没有任何帮助我的情况。我应该找到一个解决方案,它允许我用一个大查询查询示例和默认图像,而不是在for循环中进行工作。
答案 0 :(得分:1)
DEFAULT_VERSION
模块是否常数?如果是这样,您可以将version=Version.objects.get(name=DEFAULT_VERSION)
调用分解出去,这样您就不必为每个example in examples
执行此操作。
您还应该能够使用自定义Prefetch
对象:https://docs.djangoproject.com/en/1.11/ref/models/querysets/#django.db.models.Prefetch。有些事情(虽然你必须自己测试和调整):
qs = Image.filter(version=Version.objects.get(name=DEFAULT_VERSION))
pref = Prefetch('images', queryset=qs, to_attr='image')
examples = Example.objects.prefetch_related(pref)
理想情况下,您当然会将Example.default_image
设置为OneToOneField
,以便您可以有效地检索默认图像。
cached_property
装饰器在这里不会真正帮助你,因为只要实例存在,它只会缓存属性。如果您需要多次调用instance.get_default_image()
, 会有所帮助,但您仍然需要为您获取的每个实例执行一次get_default_image
。
希望有所帮助