【问题标题】:Django Postgresql JsonField query related dictionary keysDjango Postgresql JsonField 查询相关字典键
【发布时间】:2018-05-15 09:49:39
【问题描述】:

我拥有的模型的一部分,它使用 Django Model 字段,如下所示:

class SalesModel(models.Model):
    some_data = models.PositiveIntegerField(db_index=True)
    some_other_data = models.CharField(max_length=50)
    json_data = JSONField(blank=True, null=True)

下面是 JsonData 字段的格式:

[{"id": val, "contribution": "some_val", }, {"id": some_val, "contribution": "some_other_val",}, {"id": other_val, "contribution": "some_another_val"}]

即格式为:

 [{'id':XX, 'contribution':XX},{'id':YY, 'contribution':YY},{'id':ZZ, 'contribution':ZZ}]

目前我可以使用 ID 的 val 过滤 Django 表。我现在想知道那个特定 ID 的贡献。

例如,如果 val = 1,我想过滤具有 id = 1 的 JsonField 的模型 SalesModel,并且我想显示相关贡献。因此,这意味着,在 3 个可能的字典中(根据字段构造),我只会显示一个字典(由该字典的“ID”键过滤)。这意味着,如果第 2 个字典具有匹配的 ID,则仅显示第 2 个贡献,如果第 1 个 ID 匹配,则仅显示第 1 个贡献,对于第 3 个字典也是如此。

有什么办法可以做到吗?

【问题讨论】:

    标签: python django postgresql django-jsonfield postgresql-json


    【解决方案1】:

    您可以通过给它一个键值对直接为id: contribution 的dict 来以不同的方式重组您的JSONField。这样您就可以使用has_key 过滤器和KeyTransform 将起作用,因为我不确定它是否适用于一系列字典。所以假设你的json_data 看起来像这样:

    {1: 'xx', 3: 'yy', 9: 'zz'}
    

    你可以这样查询,基于@vanojx1的贡献:

    SalesModel.filter(json_data__has_key=id)\
        .annotate(contrib=KeyTransform(id, 'json_data')\
        .values('contrib')
    

    或者,在 postgresql 中使用原始 jsonb:

    SalesModel.filter(json_data__has_key=id)\
        .extra(select={'contrib': "json_data->{0}".format(id)})\
        .values('contrib')
    

    【讨论】:

    • 这听起来不错。 KeyTransform 似乎没有太多官方文档。无论哪种方式,您能否确认 KeyTransform 在 django 1.10.X 级别(x=5)上工作。 KeyTransform 实际上是什么?很抱歉在 cmets 中问了一个愚蠢的问题。如果它不好,让我谷歌并理解。
    • 不,我无法确认这适用于 Django 1.10.x,我使用的是 1.11。但是KeyTransform 在 1.10.x 上受支持,所以我很确定它可以工作。请注意,嵌套的KeyTransforms(在这种情况下不需要)仅在 Django 1.11.x 及更高版本中受支持。
    • KeyTransform, KeyTextTransforms 等...通过组合 json 文档的两个键并添加相应的 Postgresql operator to traverse the json document 来工作。所以contrib=KeyTransform('key', 'json_data') 基本上是select json_data::json -> 'key'。
    • 谢谢@dirkgroten...这似乎是一个完整的解决方案
    【解决方案2】:

    这应该可以DOC

    SalesModel.objects.filter(json_data__id=1).values('id', 'json_data__contribution')
    

    【讨论】:

    • 您确定我们可以将“值”与“postgresql”特定的“jsonfield”一起使用。让我试试。但据我所知,这会导致错误。
    • 基于通用框架查询逻辑..应该
    • django.core.exceptions.FieldError:无法将关键字“贡献”解析为字段。不允许加入“json_data”
    • @dirkgroten 没错,这就是我认为的错误,因为 Django 不允许这样做,而且我不知道 Postgres 允许的任何 Raw SQL
    • 认为应该可行...看看this 似乎完全相同的问题。如果它工作不正常将此标记为重复
    【解决方案3】:

    是的,我猜。如果我理解正确,您将有一个要匹配的 ID 或 ID 列表。所以如果你的 ID 是 2:

    my_id = 2
    dict1 = [{"id":1, "contribution":10},{"id":2, "contribution":20},{"id":3, "contribution":30}] 
    for i in dict1:
        if i["id"] == my_id:
            print(i["contribution"])
    

    【讨论】:

    • 好吧,我最多可以有大约 100K 的数据,平均大约有 20K 的数据。我不想运行 for 循环。虽然循环是一种选择,但有什么可以使用 ORM/Raw SQL 的吗?感谢@pissall 抽出宝贵时间。你会看到我们缠绕有 3N 个循环。如果它更频繁地达到 100K 标记,那就不好了。
    猜你喜欢
    • 2016-10-28
    • 1970-01-01
    • 2018-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-25
    • 2018-10-02
    • 1970-01-01
    相关资源
    最近更新 更多