1

给定一个结构为字典列表的 Django JSONField:

# JSONField "materials" on MyModel:
[
    {"some_id": 123, "someprop": "foo"},
    {"some_id": 456, "someprop": "bar"},
    {"some_id": 789, "someprop": "baz"},
]

并给出要查找的值列表:

myids = [123, 789]

我想在这些字典列表中的任何位置查询具有匹配 some_id 的所有 MyModel 实例。我可以这样做一次在字典中搜索一个:

# Search inside the third dictionary in each list:
MyModel.objects.filter(materials__2__some_id__in=myids)

但我似乎无法构建一个查询来一次在所有字典中搜索。这可能吗?

4

2 回答 2

1

包含可能会帮助你。应该是这样的:

q_keys = Q()

for _id in myids:
    q_keys |= Q(materials__contains={'some_id': _id})

MyModel.objects.filter(q_keys)
于 2019-11-21T05:53:41.150 回答
1

鉴于来自 Davit Tovmasyan 的线索,通过增加 match_targets 并建立一组Q查询来做到这一点,我编写了这个函数,它接受一个要搜索的字段名称、一个要搜索的属性名称和一个目标匹配列表。它返回一个包含匹配字典和它们来自的源对象的新列表。

from iris.apps.claims.models import Claim
from django.db.models import Q


def json_list_search(
    json_field_name: str,
    property_name: str, 
    match_targets: list
    ) -> list:
    """

    Args:
        json_field_name: Name of the JSONField to search in
        property_name: Name of the dictionary key to search against
        match_targets: List of possible values that should constitute a match

    Returns:
        List of dictionaries: [
            {"claim_id": 123, "json_obj": {"foo": "y"},
            {"claim_id": 456, "json_obj": {"foo": "z"}
        ]

    Example:
        results = json_list_search(
            json_field_name="materials_data", 
            property_name="material_id", 
            match_targets=[1, 22]
        )

        # (results truncated):
        [
            {
                "claim_id": 1,
                "json_obj": {
                    "category": "category_kmimsg",
                    "material_id": 1,
                },
            },
            {
                "claim_id": 2,
                "json_obj": {
                    "category": "category_kmimsg",
                    "material_id": 23,
                }
            },
        ]
    """

    q_keys = Q()
    for match_target in match_targets:
        kwargs = {
            f"{json_field_name}__contains": [{property_name: match_target}]
        }
        q_keys |= Q(**kwargs)

    claims = Claim.objects.filter(q_keys)

    # Now we know which ORM objects contain references to any of the match_targets 
    # in any of their dictionaries. Extract *relevant* objects and return them
    # with references to the source claim.
    results = []
    for claim in claims:
        data = getattr(claim, json_field_name)
        for datum in data:
            if datum.get(property_name) and datum.get(property_name) in match_targets:
                results.append({"claim_id": claim.id, "json_obj": datum})

    return results
于 2019-11-21T18:49:52.520 回答