仅使用数字键查询 JSONField

the*_*ire 5 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>]>
Run Code Online (Sandbox Code Playgroud)

我希望以下内容也返回“梅格”狗:

Dog.objects.filter(data__99='FINDME')
Run Code Online (Sandbox Code Playgroud)

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

Van*_*aro 5

wontfix 一张关于这个的Django 票

这是记录在案的行为

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

如果您希望查询的键与另一个查找的名称发生冲突,请改用 jsonfield.contains 查找。

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

Dog.objects.filter(data__contains={'99': 'FINDME'})
Run Code Online (Sandbox Code Playgroud)

无论如何,我建议您避免在 json 字段中使用数字键,因为使用该contains方法您无法执行startswith和其他类似的查找(请参阅此类似问题),例如Dog.objects.filter(data__99__startswith='FIND').


the*_*ire 1

看来没有什么好的选择。这是 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
Run Code Online (Sandbox Code Playgroud)

由此看来,它试图将每个键都转换为整数,并在可能的情况下使用它作为键。

您可以使用以下技巧来按照您想要的方式进行查询:

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

并像这样使用它:

jsonb_integer_keys_to_str(Dog.objects.filter(data__99='FINDME'), 99)
Run Code Online (Sandbox Code Playgroud)

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

  • 哦,我知道为什么——因为 JSON 不允许整数键 (2认同)