是否可以通过将hstore值转换为int
或float
来过滤查询集?
我遇到了一个需要向现有数据模型添加更强大查询的问题。数据模型使用HStoreField
来存储大部分建筑数据,我们需要能够对它们进行查询/过滤,并且某些值需要被视为数值。
但是,由于这些值被视为字符串,因此它们会逐字符进行比较,从而导致查询错误。例如,'700' > '1000'
。
因此,如果我想查询sqft值介于700和1000之间的所有项目,我会得到零结果,即使我可以清楚地看到有数百个项目的值在该范围内。如果我只是查询sqft值> = 700的项目,我只得到sqft值以7,8或9开头的结果。
我也尝试使用来自django-pgjson
的JsonField进行测试(因为我们还没有使用Django 1.9),但它看起来有同样的问题。
设置
Django==1.8.9
django-pgjson==0.3.1 (for jsonfield functionality)
Postgres==9.4.7
models.py
from django.contrib.postgres.fields import HStoreField
from django.db import models
class Building (models.Model):
address1 = models.CharField(max_length=50)
address2 = models.CharField(max_length=20, default='', blank=True)
city = models.CharField(max_length=50)
state = models.CharField(max_length=2)
zipcode = models.CharField(max_length=10)
data = HStoreField(blank=True, null=True)
示例数据
这是hstore字段上某些数据的示例。
address1: ...
address2: ...
city: ...
state: ...
zipcode: ...
data: {
'year_built': '1995',
'building_type': 'residential',
'building_subtype': 'single-family',
'bedrooms': '2',
'bathrooms': '1',
'total_sqft': '958',
}
返回错误结果的示例查询
queryset = Building.objects.filter(data__total_sqft__gte=700)
我已尝试使用annotate
功能来查看是否可以强制它转换为数字值,但我没有运气这么做。我总是得到一个错误,说我正在查询的字段不存在。这是我在别处找到的一个似乎不起作用的例子。
queryset = Building.objects.all().annotate(
sqft=RawSQL("((data->>total_sqft)::numeric)")
).filter(sqft__gte=700)
导致此错误:
FieldError:无法将关键字“sqft”解析为字段。选择包括:地址1,地址2,城市,州,邮政编码,数据
使这种设置更加复杂的一件事是我们动态构建查询并将Q()
个对象和/或它们一起使用。
所以,考虑到键,值和运算符类型(gte
,lte
,iexact
),尝试执行类似这样的操作:
queryset.annotate(**{key: RawSQL("((%data->>%s)::numeric)", (key,)})
queries.append(Q(**{'{}__{}'.format(key, operator): value})
queries.filter(reduce(operator.and_, queries)
但是,即使只是让第一个查询工作而不动态构建它们,我也会很高兴。
我已经考虑过必须使用明确定义的字段为建筑数据创建单独的模型,但data
hstore中有超过600个键值对。似乎将其转变为具体的数据模型将是设置和潜在维护的噩梦。
答案 0 :(得分:0)
所以我遇到了一个非常类似的问题,最终将Cast Function与KeyTextTransform(Django> 1.10)一起使用。
my_query =.query.annotate(as_numeric=Cast(KeyTextTransform('my_json_fieldname', 'metadata'), output_field=DecimalField(max_digits=6, decimal_places=2))).filter(as_numeric=2)