如何将数组传递给自定义Django数据库函数?

时间:2017-10-24 15:43:05

标签: python django

我试图像这样为PostgreSQL width_bucket编写一个Django包装器:

from django.db.models import Func
from django.db import models
class Log2Bucket(Func):
    template = "width_bucket(%(expressions)s::double precision[])"

    def __init__(self, expression, a, b, **extra):
        buckets = []
        while a <= b:
            buckets.append(str(a))
            a *= 2
        buckets = Value(buckets, output_field=ArrayField(models.FloatField()))
        super().__init__(expression, buckets,
            output_field=models.PositiveSmallIntegerField(), **extra)

这适用于像

这样的查询
Foo.objects.annotate(b=Log2Bucket('bar', 125, 100000)).values('b')

但是

from django.db.models import Count
(Foo.objects.annotate(b=Log2Bucket('bar', 125, 100000))
    .values('b').annotate(count=Count('pk')))

我从Django里面得到一个TypeError: unhashable type: 'list'

如果我使用

buckets = Value(tuple(buckets), output_field= ...

我仍然从同一个地方获得TypeError: unhashable type: 'list'

我该如何解决这个问题?是否有一种方法可以减少这种功能?

我尝试使用function = 'width_bucket'代替template = ...,但后来我收到了PostgreSQL错误function width_bucket(double precision, numeric[]) does not exist我升级到PostgreSQL 10并且这个走了。

1 个答案:

答案 0 :(得分:0)

我成功地解决了这个问题:

class Log2Bucket(Func):
function = 'width_bucket'

    def __init__(self, expression, a, b, **extra):
        buckets = []
        while a <= b:
            buckets.append(str(a))
            a *= 2
        buckets = Value("{%s}" % ','.join(map(str, buckets)))
        super().__init__(expression, buckets,
            output_field=models.PositiveSmallIntegerField(), **extra)

哪个有效,但不漂亮。