Djang或json数据的Django数值比较?

时间:2016-04-21 23:14:45

标签: python django postgresql hstore

是否可以通过将hstore值转换为intfloat来过滤查询集?

我遇到了一个需要向现有数据模型添加更强大查询的问题。数据模型使用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()个对象和/或它们一起使用。

所以,考虑到键,值和运算符类型(gtelteiexact),尝试执行类似这样的操作:

queryset.annotate(**{key: RawSQL("((%data->>%s)::numeric)", (key,)})
queries.append(Q(**{'{}__{}'.format(key, operator): value})
queries.filter(reduce(operator.and_, queries)

但是,即使只是让第一个查询工作而不动态构建它们,我也会很高兴。

我已经考虑过必须使用明确定义的字段为建筑数据创建单独的模型,但data hstore中有超过600个键值对。似乎将其转变为具体的数据模型将是设置和潜在维护的噩梦。

1 个答案:

答案 0 :(得分:0)

所以我遇到了一个非常类似的问题,最终将Cast FunctionKeyTextTransform(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)