Django用数字键postgres Json字段

时间:2018-05-28 15:49:30

标签: django django-postgresql

我有postgres json字段的模型。

class MyModel(models.Model):
    data = JSONField(null=True)

然后,我这样做:

m1 = MyModel.objects.create(data={'10':'2017-12-1'})
m2 = MyModel.objects.create(data={'10':'2018-5-1'})

我想查询其键'10'以'2017'开头的所有MyModel,所以我想写:

MyModel.objects.filter(data__10__startswith='2017')

问题是10被解释为整数,因此,在生成的查询中,它被视为列表索引而不是键。 反正有没有解决这个问题? (写出原始查询除外)。

这是生成的查询:

SELECT "systools_mymodel"."id", "systools_mymodel"."data" FROM "systools_mymodel" WHERE ("systools_mymodel"."data" ->> 10)::text LIKE '2017%' LIMIT 21;

我希望引用10(这会给我正确答案)。

谢谢!

1 个答案:

答案 0 :(得分:0)

一个非常hackish的解决方案(使用自己的风险,在Django 2.0.5下测试,无效保修......):

# patch_jsonb.py
from django.contrib.postgres.fields.jsonb import KeyTransform


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 %s %%s)" % (lhs, self.nested_operator), [
            key_transforms] + params
    try:
        int(self.key_name)
    except ValueError:
        if self.key_name.startswith("K") and self.key_name[1:].isnumeric():
            lookup = "'%s'" % self.key_name[1:]
        else:
            lookup = "'%s'" % self.key_name
    else:
        lookup = "%s" % self.key_name
    return "(%s %s %s)" % (lhs, self.operator, lookup), params


def patch():
    KeyTransform.as_sql = as_sql

用法:

  1. 将其添加到settings.py

    的底部
    import patch_jsonb
    patch_jsonb.patch()
    
  2. 使用__123__查找代替__K123__次查找 - 此修补程序将剥离大写K

    MyModel.objects.filter(data__K10__startswith='2017')
    
  3. 考虑避免使用数字作为jsonb对象键......