Django在ForeignKey上正确使用select_related或prefetch_related

时间:2014-03-29 17:39:30

标签: django optimization

我试图弄清楚如何使用select_relatedprefetch_related来优化来自外键另一端的查询。例如:

说我有一些模型如下:

class Product(models.Model):
    name = models.CharField(max_length=50)

class ProductImage(models.Model):
    image = models.ImageField(upload_to="images")
    product = models.ForeignKey("myapp.Product", related_name="product_images")

如果我想要打印所有ProductImage,我必须遍历所有产品,然后,对于每个产品,迭代所有产品图像。但是,这会产生O(n * m)个数据库查询。

for product in Product.objects.all():
    print product.product_images.all()

我想知道是否有办法利用select_relatedprefetch_related将此查询减少到一个或两个查询。

正如Django所记录的那样,当我选择select_related时,我可以让ProductImage工作。如您所见,添加select_related会在产品表中创建JOIN。

>>> ProductImage.objects.all().query.sql_with_params()
(u'SELECT "myapp_productimage"."id",  ...  FROM "myapp_productimage" .... 

>>> ProductImage.objects.select_related('product').query.sql_with_params()
(u'SELECT "myapp_productimage"."id", ...   FROM "myapp_productimage" LEFT OUTER JOIN ....

但是,如何完成相反的操作呢?例如,如何查询所有Product并将其加入ProductImage s?做类似以下的事情似乎无法奏效。

Product.objects.select_related('product_images').query.sql_with_params()

在Django中是否可以这样?

感谢您的考虑。

2 个答案:

答案 0 :(得分:8)

这正是prefetch_related的作用。

Product.objects.prefetch_related('product_images')

但是,使用query.sql_with_params()进行诊断是没有意义的:prefetch_related执行两个查询,第二个查询不会显示在那里。您应该使用django.db.connection.queries来检查Django正在进行的查询,或者更好地使用Django调试工具栏向您显示。

答案 1 :(得分:3)

当您具有正向外键关系时使用

select_related,并且当存在反向外键关系时应使用prefetch_related