【问题标题】:Django query reverse relationship without foreign keyDjango查询没有外键的反向关系
【发布时间】:2014-02-16 00:06:36
【问题描述】:

我目前正在开发一个 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(...)
    ...

使用这种方法而不是使用外键(或通用外键)链接到源图像允许将多个MultiresImageFields 添加到源模型:

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 进行性能优化,但是在这里我不能,因为我的多分辨率模型没有源模型的外键,因此源模型上不存在反向关系。

问题

所以我的问题是如何优化上述查询?

目前的一些想法

  • 由于我正在制作自定义模型字段,我可以使用 contribute_to_class 手动将反向关系添加到源模型,但我不知道如何?
  • 另一种方法是在Django>=1.7 中使用Prefetch API,但我不知道如何使它也能正常工作。

【问题讨论】:

  • 如果没有关于您的自定义 MuliResImageField 的更多信息,很难给您正确的建议。无论如何我不明白的是:为什么你不能使用简单的 ForeignKey。例如。图像=模型。外键(多分辨率图像); image2=models.ForeignKey(MultiResImage).
  • 我希望 MultiresImageField 向后兼容常规 Django 图像字段。您可以在 github 上查看 MultiresImageField 的当前代码,但我认为其中任何一个都与解决此查询问题无关。

标签: django django-models django-queryset


【解决方案1】:

最简单但可能不是最优雅的解决方案是编写一种辅助类:

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)

好吧,一个更优雅的解决方案可能与您对contribute_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')

然后像这样修改contribute_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)

然后通过信号处理管理关系的细节(就像你已经做的那样)。

【讨论】:

  • 这就是我现在所做的。我希望找到一种方法来连接到 Django 的 API。无论如何,谢谢。
猜你喜欢
  • 2020-01-02
  • 1970-01-01
  • 2011-12-06
  • 1970-01-01
  • 2020-02-12
  • 2014-07-22
  • 2012-10-10
  • 2019-05-18
  • 2022-01-22
相关资源
最近更新 更多