Django ORM сложный order_by (с CASE и WHEN по связанной модели), DISTINCT, оставляющий дубликаты
Что я пытаюсь сделать - это создать ajax таблицу данных с обработкой на стороне сервера, где в первой колонке будет имя_ключевого слова (основная модель), а во всех остальных - позиции рангов (связанная модель), с возможностью сортировки данных по любой колонке, применения поиска и фильтров
Таблица выглядит следующим образом:
| Keyword_name | Position created 01.01.21, Google search engine, New-York Region | Position created 03.01.21, Yahoo Search engine, New-York Region | blablabla, all types of positions by dates,engines,regions | |
|--------------|------------------------------------------------------------|---------------------------------------------------------|------------------------------------------------------------|---|
| keyword 1 | rank:1 | Rank:3 | etc | |
| keyword 2 | rank:5 | Rank:1 | etc | |
| etc.. | | | | |
Мои модели:
class Keyword():
"""Main model"""
project = models.ForeignKey(Project, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
name = models.CharField(max_length=1000)
<----->
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now_add=True)
class Position():
"""Model for keep keywords positions"""
keyword = models.ForeignKey(Keyword, on_delete=models.CASCADE)
engine = models.ForeignKey(SearchEngine, on_delete=models.CASCADE)
region = models.ForeignKey(Region, on_delete=models.CASCADE)
rank = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now_add=True)
Итак, у меня нет проблем с фильтрацией позиций (получение данных с помощью простого Keyword.objects.filter("position__created_at__date_gte" = 01.01.21, "position__created_at__date_lte" = 01.02.21 и т.д.), но проблема в том, что я пытаюсь сортировать данные по столбцам - я хочу щелкнуть на столбце и упорядочить данные по рангу выбранного столбца.
Мой текущий код для сортировки данных по столбцам таков:
---on column click get clicked column params
column_info = 'some_date|Google|New-York'.split(|)
date = column_info[0]
engine = SearchEngine.objects.get(name=column_info[1])
region = Region.objects.get(name=column_info[2])
kwargs = {
'{0}__{1}'.format('position', 'engine'): engine,
'{0}__{1}'.format('position__created_at', 'date'): date,
'{0}__{1}'.format('position', 'region'): region
}
#making complex order ("position__rank=0, then=999" is for zero positions, to make them last in order)
custom_order = Case(When(**kwargs, position__rank=0, then=999), When(**kwargs, then='position__rank'))
#returning ordered queryset
return Keyword.objects.filter(*filter_params_from_datatable).order_by(custom_order).distinct()
Но проблема в том, что возвращаемый набор запросов имеет дубликаты ключевых слов (количество ключевых слов в наборе корректно, но некоторые ключевые слова отсутствуют, а некоторые ключевые слова из начала таблицы дублируются в конце таблицы), так что я предполагаю, что это причина отличия по связанному полю.
Я пытался указать поле disctinct (я использую PostgreSQL) как Keyword.objects.filter(*filter_params_from_datatable).order_by(custom_order).distinct(pk
), но я получаю ERROR: SELECT DISTINCT ON expressions must match initial ORDER BY expressions
, что я нашел в официальных документах:
Когда вы указываете имена полей, вы должны предоставить order_by() в QuerySet, и поля в order_by() должны начинаться с полей в order_by(), в том же порядке. distinct(), в том же порядке.
Но как можно поместить мой сложный CASE WHEN порядок в distinct?
Или может быть мой способ решения этой проблемы неверен, и есть гораздо более простой и эффективный способ?
Я борюсь с этой проблемой уже более двух недель, прошу ЛЮБОЙ помощи, совета, что угодно, буду признателен (и извините, если мое объяснение проблемы трудно для понимания, английский не мой родной язык)