如何基于多对多关联字段的组合字符串排序Django QuerySet
解决方案(PostgreSQL环境)
要实现你的需求,核心是先按中间表FooBars的date排序关联的Bar,再将这些Bar的name拼接成字符串,最后以此字符串排序Foo的QuerySet。可以直接用Django内置的StringAgg聚合函数完成:
from django.db.models import StringAgg # 注解并排序Foo QuerySet sorted_foos = Foo.objects.annotate( # 按FooBars的date排序,拼接Bar的name为字符串 sorted_bars_str=StringAgg( 'foobars__bar__name', separator=', ', order_by='foobars__date' ) ).order_by('sorted_bars_str')
代码说明:
StringAgg:Django针对PostgreSQL提供的字符串拼接聚合函数,支持指定拼接时的排序规则。'foobars__bar__name':通过中间表FooBars关联到Bar的name字段。order_by='foobars__date':指定拼接时按照中间表的date字段排序Bar。annotate:将拼接后的字符串作为sorted_bars_str字段附加到每个Foo对象上。order_by('sorted_bars_str'):最终按拼接后的字符串对Foo进行排序。
对应你给出的示例数据,执行后会得到:
- Foo b的
sorted_bars_str为alpha, beta, gamma - Foo a的
sorted_bars_str为alpha, gamma, beta
按字符串字典序排序后,Foo b会排在Foo a之前,完全符合预期。
MySQL环境适配
如果你的项目使用MySQL,Django未内置对应聚合函数,需要自定义GROUP_CONCAT函数来实现:
from django.db.models import Func class GroupConcat(Func): function = 'GROUP_CONCAT' template = "%(function)s(%(expressions)s ORDER BY %(order_by)s SEPARATOR '%(separator)s')" # 使用自定义函数注解并排序 sorted_foos = Foo.objects.annotate( sorted_bars_str=GroupConcat( 'foobars__bar__name', order_by='foobars__date', separator=', ' ) ).order_by('sorted_bars_str')
额外修正提示:
- 模型中
ManyToManyField关联的应为Bar而非Bars; auto_now_add参数值需为大写True;CharField必须指定max_length参数,否则模型迁移会报错。修正后的模型代码:
class Bar(models.Model): name = models.CharField(max_length=100) class Foo(models.Model): name = models.CharField(max_length=100) bars = models.ManyToManyField(Bar, through='FooBars') class FooBars(models.Model): date = models.DateTimeField(auto_now_add=True) foo = models.ForeignKey(Foo, on_delete=models.CASCADE) bar = models.ForeignKey(Bar, on_delete=models.CASCADE)
内容的提问来源于stack exchange,提问作者Jack Siman
相关产品推荐
相关产品推荐

