Django подсчитывает количество комментариев в JSONField с помощью Postgres - PullRequest
2 голосов
/ 08 марта 2019

Используя Django, у меня есть поле, которое имеет тип JSONField. Я хочу получить четкое количество вложенных ключей / значений в JSON. С обычным полем вы можете просто сделать что-то вроде следующего

model.objects.values('field_name')\
.annotate(total=Count('field_name')).order_by('-total')

Это не работает для JSONField.

Пример модели

class Pet(models.Model):
    data = JSONField()

Пример данных

  {
    'name':'sparky',
    'animal':'dog',
    'diet':{
        'breakfast':'biscuits',
        'dinner':'meat',
    }
}

Попытка

Pet.objects.values('data__diet__dinner')\
.annotate(total=Count('data__diet__dinner')).order_by('-total')

Исключение

TypeError: unhashable type: 'list'

Как правильно выполнить это?

1 Ответ

2 голосов
/ 09 марта 2019

Вы можете использовать jsonb_extract_path_text через объект Func в качестве альтернативы преобразованию поля:

Pet.annotate(dinner=Func(
    F('data'), Value('diet'), Value('dinner'),
    function='jsonb_extract_path_text'))  \
.values('dinner')  \
.annotate(total=Count('dinner'))

Причиной сбоя преобразования поля data__diet__dinner является ошибка в Django, когда вы переходите глубже, чем на один уровень, в структуру json и используют GROUP BY в SQL. Первый уровень (name, animal, diet) должен работать нормально.

Причина, по-видимому, заключается в том, что для вложенных преобразований Django изменяет используемый синтаксис SQL, переключаясь с одного значения на список, чтобы указать путь к структуре json.

Это синтаксис, используемый для не вложенных преобразований json (= первый уровень):

"appname_pet"."data" -> 'diet'

И это синтаксис, используемый для вложенных преобразований (глубже, чем первый уровень):

"appname_pet"."data" #> ARRAY['diet', 'dinner']

При создании запроса Django задыхается в этом списке, вырабатывая необходимые условия GROUP BY. Это не кажется неизбежным ограничением; поддержка преобразований является довольно новой, и это, возможно, один из недостатков, которые еще не были разработаны. Так что, если вы откроете билет Django , это может просто сработать на несколько версий.

...