仅使用数字键查询JSONField

时间:2017-05-10 22:02:01

标签: python django

我从Django的文档中采用了以下示例,除了替换后添加了一个键' 99':

>>> Dog.objects.create(name='Rufus', data={
... 'breed': 'labrador',
...     'owner': {
...         'name': 'Bob',
...         'other_pets': [{
...             'name': 'Fishy',
...         }],
...     },
... })
>>> Dog.objects.create(name='Meg', data={'breed': 'collie', '99': 'FINDME',})

>>> Dog.objects.filter(data__breed='collie')
<QuerySet [<Dog: Meg>]>

我希望以下内容也能让&#34; Meg&#34;狗:

Dog.objects.filter(data__99='FINDME')

然而,似乎因为我的键是一个整数,Django没有正确处理它。我如何在python jsonfields中使用字符串的整数键?

2 个答案:

答案 0 :(得分:1)

有一个wontfix Django ticket

它是documented behavior

  

如果键是整数,它将被解释为数组中的索引查找。

     

如果要查询的键与另一个查询的名称冲突,请使用jsonfield.contains查找。

所以建议的解决方案是使用jsonfield.contains

Dog.objects.filter(data__contains={'99': 'FINDME'})

无论如何,我建议您避免在json字段中使用数字键,因为使用contains方法无法执行startswith和其他类似的查找(请参见similar question),例如{{1} }。

答案 1 :(得分:0)

看起来不是一个不错的选择。这是来自django / contrib / postgres / fields / jsonb.py的片段:

def as_sql(self, compiler, connection):
    key_transforms = [self.key_name]
    previous = self.lhs
    while isinstance(previous, KeyTransform):
        key_transforms.insert(0, previous.key_name)
        previous = previous.lhs
    lhs, params = compiler.compile(previous)
    if len(key_transforms) > 1:
        return "{} #> %s".format(lhs), [key_transforms] + params
    try:
        int(self.key_name)
    except ValueError:
        lookup = "'%s'" % self.key_name
    else:
        lookup = "%s" % self.key_name
    return "%s -> %s" % (lhs, lookup), params

从这看起来,它似乎试图将每个键变成一个整数,并在可能的情况下将其用作键。

以下是您可以按照自己的方式进行查询的黑客行为:

def jsonb_integer_keys_to_str(qs, key):
  return qs.model.objects.raw(qs.query.__str__().replace('-> {}'.format(key), "-> '{}'".format(key)))

并按原样使用:

jsonb_integer_keys_to_str(Dog.objects.filter(data__99='FINDME'), 99)

此解决方案过于具体,但在这种情况下可以使用。它正在做的是修改postgres sql并将引号放在正确的位置。