Django通用关系和ORM查询

7

假设我有以下模型:

class Image(models.Model):
    image   = models.ImageField(max_length=200, upload_to=file_home)
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey()

class Article(models.Model):
    text = models.TextField()
    images = generic.GenericRelation(Image)

class BlogPost(models.Model):
    text = models.TextField()
    images = generic.GenericRelation(Image)

什么是最高效的处理器和内存方式,以查找至少有一张图片附加在其中的所有文章?

我已经做了这个:

Article.objects.filter(pk__in=Image.objects.filter(content_type=ContentType.objects.get_for_model(Article)).values_list('object_id', flat=True))

这个方法可行,但除了难看之外,还需要很长时间。

我猜想使用原始SQL可能有更好的解决方案,但那超出了我的能力范围。就以上面的代码而言,所生成的SQL如下:

 SELECT `issues_article`.`id`, `issues_article`.`text` FROM `issues_article` WHERE `issues_article`.`id` IN (SELECT U0.`object_id` FROM `uploads_image` U0 WHERE U0.`content_type_id` = 26 ) LIMIT 21

编辑: czarchaic的建议语法更加优雅,但性能更差(更慢)。他的查询生成的SQL如下:

SELECT DISTINCT `issues_article`.`id`, `issues_article`.`text`, COUNT(`uploads_image`.`id`) AS `num_images` FROM `issues_article` LEFT OUTER JOIN `uploads_image` ON (`issues_article`.`id` = `uploads_image`.`object_id`) GROUP BY `issues_article`.`id` HAVING COUNT(`uploads_image`.`id`) > 0  ORDER BY NULL LIMIT 21

编辑: 感谢Jarret Hardie!以下是他显而易见的解决方案生成的SQL代码:

SELECT DISTINCT `issues_article`.`id`, `issues_article`.`text` FROM `issues_article` INNER JOIN `uploads_image` ON (`issues_article`.`id` = `uploads_image`.`object_id`) WHERE (`uploads_image`.`id` IS NOT NULL AND `uploads_image`.`content_type_id` = 26 ) LIMIT 21

这是您实际的模型结构,还是为了简单起见,在您的问题示例中没有表示整个类层次结构?我之所以问这个问题,是因为这个特定的示例根本不需要泛型。 - Jarret Hardie
不,这是一个简化和精简的模型结构。 - hanksims
1
虽然您接受的答案非常有效,但我很想知道如果您需要的不仅仅是“至少一个图像”,解决方案将是什么。 - czarchaic
1
这是我的特定用例,所以我没有真正考虑过。也许,如果我想要至少有2张图片的所有文章呢?看起来你可能想使用你概述的方法--但是你必须缓存它,因为如果你的系统中有任何重要数量的图片或文章,那么这将是一个长时间运行的查询。 - hanksims
2个回答

6
感谢通用关系,您应该能够使用传统的逆向关系查询集语义来查询此结构:
Article.objects.filter(images__isnull=False)

这会为与多个图像相关的任何“文章”生成重复项,但您可以使用“distinct()”查询集方法消除它们。
Article.objects.distinct().filter(images__isnull=False)

我认为我们有一个胜利者!出于完整起见,我将在另一个编辑中发布生成的SQL。跑得非常快,而且is_null=False。有时候最简单的事情就在你的眼前。 - hanksims
谢谢 hanksims... 希望一切顺利 :-) 我承认我还没有看过 SQL,所以肯定很好奇。 - Jarret Hardie

1

我想你最好使用聚合

from django.db.models import Count

Article.objects.annotate(num_images=Count('images')).filter(num_images__gt=0)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接