You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Django ORM实现含图片聚合的多表关联查询

问题描述

现有以下Django模型:

class Mark(models.Model):
  name = models.CharField(max_length=200, verbose_name="Марка", unique=True, null=True)
  #...

class Model(models.Model):
  mark = models.ForeignKey(Mark, on_delete=models.CASCADE, verbose_name="Марка")
  name = models.CharField(max_length=200, verbose_name="Модель", unique=True, null=True)
  #...

class Car(models.Model):
  vin = models.CharField(max_length=100, null=True)
  #...

class Image(models.Model):
  car = models.ForeignKey(Car, related_name='images', on_delete=models.CASCADE)
  image = models.ImageField(upload_to=get_upload_path, verbose_name="Фото")
  #...

需要用Django ORM替代如下原生SQL查询(禁止使用raw方法):

query = "SELECT c.*, m.name mark_name, JSON_ARRAYAGG(img.image) image FROM incarcatalog_car c \
LEFT JOIN incarcatalog_mark m ON m.id=c.mark_id \
LEFT JOIN incarcatalog_image img ON img.car_id=c.id \
WHERE c.id>%s \
GROUP BY c.id;"
queryset = Car.objects.raw(query, [id])

目前已通过queryset = Car.objects.select_related("mark", "model").all()实现Mark、Model表的左关联,但不知道如何关联Image表并实现图片聚合。

解决方案

要实现原生SQL中的图片聚合效果,结合annotate()和Django聚合函数即可完成,具体实现分版本处理:

Django 3.1及以上版本(支持JSON数组聚合)

  1. 先导入所需模块:
from django.db.models import F, Value
from django.db.models.functions import JSONArrayAgg
  1. 构造ORM查询:
# 替换your_id_value为实际的id阈值
queryset = Car.objects.filter(id__gt=your_id_value) \
    .select_related('mark') \
    .annotate(
        mark_name=F('mark__name'),
        image=JSONArrayAgg(F('images__image'), default=Value('[]'))
    )

关键说明

  • filter(id__gt=your_id_value) 对应原生SQL的WHERE c.id>%s条件;
  • select_related('mark') 维持左关联Mark表的逻辑,避免N+1查询;
  • annotate(mark_name=F('mark__name')) 将Mark表的name字段别名成mark_name,对应原生SQL的m.name mark_name;
  • JSONArrayAgg(F('images__image'), default=Value('[]')) 实现JSON_ARRAYAGG(img.image)的聚合效果,default参数确保无图片的Car对象返回空数组,和原生LEFT JOIN行为一致;
  • 通过images__image利用模型中定义的related_name关联到Image表的image字段。

Django 3.1以下版本(使用普通数组聚合)

如果版本不支持JSONArrayAgg,可以用ArrayAgg替代,返回普通数组而非JSON格式:

from django.db.models import ArrayAgg, F, Value

queryset = Car.objects.filter(id__gt=your_id_value) \
    .select_related('mark') \
    .annotate(
        mark_name=F('mark__name'),
        image=ArrayAgg(F('images__image'), default=Value([]))
    )

内容的提问来源于stack exchange,提问作者Aleksej Babushkin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 14:34:52