Django JSONField список диктов «содержит» фильтр внутри подзапроса - PullRequest
0 голосов
/ 08 января 2019

Я пытаюсь отфильтровать объекты модели, которые содержат пару ключ-значение из OuterRef в их JSONField со списком диктов.

У меня есть модель EntityObject с полями «тип» и «данные».

Работает без подзапроса:

EntityObject.objects.filter(
    type='child', 
    data__list_of_dicts__contains=[{'some_key': 'value'}]
)

Также он работает внутри подзапроса без __contains (но для аннотирования нужны значения из JSONField). Например, я могу сосчитать сущности, у которых data.some_other_key равен data.some_key родительской сущности:

(EntityObject.objects
 .filter(type='parent')
 .annotate(data_some_key=Cast(
    KeyTextTransform('some_key', 'data'), models.CharField())
 )
 .annotate(cnt=Subquery(
    EntityObject.objects
        .filter(type='child')
        .annotate(data_some_other_key=Cast(
           KeyTextTransform('some_other_key', 'data'), models.CharField())
        )
        .filter(data_some_other_key=OuterRef('data_some_key')
        .values('type')
        .order_by()
        .annotate(cnt=Count('*'))
        .values('cnt')[:1],
    output_field=models.IntegerField()
))

Теперь вот что я пытаюсь сделать:

(EntityObject.objects
 .filter(type='parent')
 .annotate(data_some_key=Cast(
    KeyTextTransform('some_key', 'data'), models.CharField())
 )
 .annotate(data_some_other_key=Cast(
    KeyTextTransform('some_other_key', 'data'), models.CharField())
 )
 .annotate(cnt=Subquery(
    EntityObject.objects
        .filter(type='child')
        .annotate(data_list_of_dicts=KeyTransform('list_of_dicts', 'data')) #  not sure if this is correct
        .filter(data_list_of_dicts__contains=[{OuterRef('data_some_key'): OuterRef('data_some_other_key')}]
        .values('type')
        .order_by()
        .annotate(cnt=Count('*'))
        .values('cnt')[:1],
    output_field=models.IntegerField()
))

Но выдает ошибку:

TypeError: keys must be a string

Даже если я удаляю OuterRef из ключа, я получаю еще одну ошибку:

(EntityObject.objects
 .filter(type='parent')
 .annotate(data_some_key=Cast(
    KeyTextTransform('some_key', 'data'), models.CharField())
 )
 .annotate(data_some_other_key=Cast(
    KeyTextTransform('some_other_key', 'data'), models.CharField())
 )
 .annotate(cnt=Subquery(
    EntityObject.objects
        .filter(type='child')
        .annotate(data_list_of_dicts=KeyTransform('list_of_dicts', 'data')) #  not sure if this is correct
        .filter(data_list_of_dicts__contains=[{'another_key': OuterRef('data_some_other_key')}]
        .values('type')
        .order_by()
        .annotate(cnt=Count('*'))
        .values('cnt')[:1],
    output_field=models.IntegerField()
))

Я получаю еще одну ошибку:

TypeError: OuterRef(data_some_other_key) is not JSON serializable

Есть ли способ сделать это?

Обновление № 1

Пытался аннотировать OuterRef перед использованием:

(EntityObject.objects
 .filter(type='parent')
 .annotate(data_some_key=Cast(
    KeyTextTransform('some_key', 'data'), models.CharField())
 )
 .annotate(data_some_other_key=Cast(
    KeyTextTransform('some_other_key', 'data'), models.CharField())
 )
 .annotate(cnt=Subquery(
    EntityObject.objects
        .filter(type='child')
        .annotate(data_list_of_dicts=KeyTransform('list_of_dicts', 'data')) #  not sure if this is correct
        .annotate(parent_some_key=OuterRef('data_some_key')
        .annotate(parent_some_other_key=OuterRef('data_some_other_key')
        .filter(data_list_of_dicts__contains=[{F('parent_some_key'): F('parent_some_other_key')}]
        .values('type')
        .order_by()
        .annotate(cnt=Count('*'))
        .values('cnt')[:1],
    output_field=models.IntegerField()
))

Теперь я получаю сообщение об ошибке:

AttributeError: 'ResolvedOuterRef' object has no attribute 'contains_aggregate'

Что относится к https://code.djangoproject.com/ticket/28621

Обновление № 2

Попытка фильтрации по более простому динамическому значению. Наличие тех же ошибок, что и с OuterRef:

(EntityObject.objects
 .filter(data_list_of_dicts__contains=[{F('field1'): F('field2')}])
)

Выдает:

TypeError: keys must be a string

И только динамическое значение:

(EntityObject.objects
 .filter(data_list_of_dicts__contains=[{'some_key': F('field')}])
)

Выдает:

TypeError: F(id) is not JSON serializable

Создан еще один вопрос для этого Динамический Django JSONField содержит и поиск ключей

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...