Создайте дополнительный столбец в наборе запросов Django на основе JSON - PullRequest
0 голосов
/ 22 февраля 2019

это очень специфическая проблема.

В моей модели есть JSON с именем «content», в этом JSON есть ключ с именем «name».Моя цель - перенести имя в новый столбец набора запросов, но это кажется трудным делом, потому что content__name в некоторых случаях может не существовать, и если вы просто выберете его с помощью extra, он выдастисключение.

queryset = queryset.extra(select={'_content_name': "SELECT content->>'name'"})

Конечный результат должен включать как те, которые содержат имя, так и те, которые не содержат, те, которые не могут быть заменены, и символ, похожий на '-'или быть полностью пустым. Окончательный результат должен быть набором запросов, а не набором RawQueryset.

Вещи, которые я пытался и не совсем работал:

  1. Использование фильтрации до и попыткаобъединение с разницей оригинала, объединение невозможно, так как набор запросов имеет разное количество столбцов или столбцы разных типовВы не можете объединить content__name, который является JSON, в тот же столбец, что и content->>'name', который является строкой.
qs = queryset.filter(~Q(content__name__iexact='')).values_list('content__name')
qs2 = queryset.difference(qs).extra(select={'_item_name': "SELECT content->>'name'"}).values_list('_item_name')
queryset = qs.union(qs2)

В этом случае можно также сказать, что _item_nameневерный столбец в values_list даже после использования extra для его создания.

Попытка с использованием выражения F, не совсем работала, потому что набор запросов немного запутался при сравнении JSON: queryset.annotate(_item_name=F('content__name'))

Попытка с использованием RawSQL, но это не такработать над сценарием, который я использую (Django Admin).

Суммируя, мне нужен один из следующих пунктов, чтобы он, вероятно, работал:

  • Способобъединение двух наборов запросов, которые имеют разные типы значений / значений
  • Способ аннотировать F, даже если содержимое является JSON
  • , или способ конвертировать RawQuerysets в обычный набор запросов без потери лишних столбцов.

1 Ответ

0 голосов
/ 22 февраля 2019

Объект F, не работающий с JSONField, был задокументирован здесь вместе с обходным решением кто-то разместил в комментариях :

class KeyTextTransformFactory:

    def __init__(self, key_name):
        self.key_name = key_name

    def __call__(self, *args, **kwargs):
        return KeyTextTransform(self.key_name, *args, **kwargs)

class JSONF(F):

    def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
        rhs = super().resolve_expression(query, allow_joins, reuse, summarize, for_save)

        field_list = self.name.split(LOOKUP_SEP)
        for name in field_list[1:]:
            rhs = KeyTextTransformFactory(name)(rhs)
        return rhs

Необходимоinclude Cast в rhs,

Sample.objects.filter(jsonfield__lookup__value=Cast(JSONF('value'), 
    IntegerField()))

Пропущенный импорт из вышеупомянутого поста:

from django.db.models.functions import Cast
from django.db.models import CharField
from django.contrib.postgres.fields.jsonb import KeyTextTransform 

И переменная LOOKUP_SEP, вероятно, должна быть '__'.

Как говорится, я попробовал это, и это работает для вашего случая аннотации.Кроме того, возможно, что Cast не требуется, если тип, который вы ожидаете, не является чем-то отличным от str, я использовал его с и без Cast на строковом значении в JSONField, и он, казалось, работал должным образом.Не знаю, почему автор решил написать класс KeyTextTransformFactory, поскольку вы можете просто позвонить KeyTextTransform(name, rhs) напрямую.

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