【问题标题】:Query JSONField for with keys of numerals only仅使用数字键查询 JSONField
【发布时间】:2017-05-10 22:02:00
【问题描述】:

我从 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>]>

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

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

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

【问题讨论】:

    标签: python django


    【解决方案1】:

    关于这个有一个wontfixDjango ticket

    documented behavior:

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

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

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

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

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

    【讨论】:

      【解决方案2】:

      看起来没有什么好的选择。这是来自 django/contrib/postgres/fields/jsonb.py 的 sn-p:

      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 并将引号放在正确的位置。

      【讨论】:

      • 你为什么不把键也存储为一个整数呢?而不是'99': 'FINDME' 使用99: 'FINDME'data={'breed': 'collie', 99: 'FINDME',}
      • 好问题!结果 django 将所有 json 键转换为字符串 :(。我不知道为什么。
      • 哦,我知道为什么——因为 JSON 不允许整数键
      • 对了,你的 Django 版本是多少?
      • 对于未来的读者和您@theicfire,如果您只想访问(键,值),这将起作用,整数键的内部功能是interpreted as an index lookup in an array(更多here ) 因此,使用上述技巧,您将无法执行诸如data__owner__other_pets__0__name 之类的查询。所以最好避免使用整数键。
      猜你喜欢
      • 2018-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-19
      • 1970-01-01
      • 1970-01-01
      • 2016-12-24
      • 2016-10-28
      相关资源
      最近更新 更多