she*_*esh 3 django filter django-queryset django-rest-framework array-agg
class Language(models.Model):
iso_code = models.CharField()
class Publisher(models.Model)
name = models.CharField()
class Book(modle.Model):
name = models.CharField()
language = models.ForeignKey(Language)
publisher = models.ForeignKey(Publisher, related_name='books')
lang_ids = [1,2]
qs = Publisher.objects.annotate(
x=ArrayAgg(
Case(
When(
books__language__in=lang_ids,
then="books__name"
)
)
)
)
Run Code Online (Sandbox Code Playgroud)
我想过滤 qs,如下所示 - https://docs.djangoproject.com/en/3.1/ref/contrib/postgres/fields/#len
qs.filter(x__len=2)
Run Code Online (Sandbox Code Playgroud)
为什么这样就无法过滤qs呢?我收到错误 IndexError:元组索引超出范围。
ArrayAgg 中的输出字段是 ArrayField
class ArrayAgg(OrderableAggMixin, Aggregate):
function = 'ARRAY_AGG'
template = '%(function)s(%(distinct)s%(expressions)s %(ordering)s)'
allow_distinct = True
@property
def output_field(self):
return ArrayField(self.source_expressions[0].output_field)
def convert_value(self, value, expression, connection):
if not value:
return []
return value
Run Code Online (Sandbox Code Playgroud)
为了获取 ArrayField 的长度,您需要使用不同的函数,有cardinality和array_length。更多信息在这里https://www.postgresql.org/docs/current/functions-array.html
如果你只有一维数组,你可以使用cardinality
from django.db import models
class ArrayLength(models.Func):
function = 'CARDINALITY'
qs = Publisher.objects.annotate(
x=ArrayAgg(
Case(
When(
books__language__in=lang_ids,
then="books__name"
)
)
)
)
qs = qs.annotate(x_len=ArrayLength('x')) # notice `x_len` is just a name, you can use anything
qs = qs.filter(x_len=2) # the actual filter
Run Code Online (Sandbox Code Playgroud)