Применение union () к той же модели не распознает порядок с помощью GenericRelation - PullRequest
3 голосов
/ 05 октября 2019

У меня есть модель Article, подобная этой

from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from hitcount.models import HitCountMixin, HitCount

class Article(models.Model):
    title = models.CharField(max_length=250)
    hit_count_generic = GenericRelation(
    HitCount, object_id_field='object_pk',
    related_query_name='hit_count_generic_relation')

, когда я Article.objects.order_by('hit_count_generic__hits'), я получаю результаты. Но когда я

articles_by_id = Article.objects.filter(id__in=ids).annotate(qs_order=models.Value(0, models.IntegerField()))
articles_by_name = Article.objects.filter(title__icontains='sports').annotate(qs_order=models.Value(1, models.IntegerField()))
articles = articles_by_id.union(articles_by_name).order_by('qs_order', 'hit_count_generic__hits')

получаю ошибку

Термин ORDER BY не соответствует ни одному столбцу в наборе результатов

Как я могу добиться такого объединения? Мне пришлось использовать союз вместо AND и OR, потому что мне нужно сохранить порядок. то есть;на первом месте должно быть article_by_id, а на втором месте - article_by_name.

с использованием Django hitcount для hitcount https://github.com/thornomad/django-hitcount. Модель Hitcount приведена ниже. ,

articles_by_id = Article.objects.prefetch_related('hit_count_generic').filter(id__in=[1, 2, 3]).annotate(qs_order=models.Value(0, models.IntegerField()))
articles_by_name = Article.objects.prefetch_related('hit_count_generic').filter(title__icontains='date').annotate(qs_order=models.Value(1, models.IntegerField()))

запрос prefetch_related, когда отмечен, вообще не выбирает счетчик обращений, см.

 SELECT "articles_article"."id", "articles_article"."created", "articles_article"."last_changed_date", "articles_article"."title", "articles_article"."title_en", "articles_article"."slug", "articles_article"."status", "articles_article"."number_of_comments", "articles_article"."number_of_likes", "articles_article"."publish_date", "articles_article"."short_description", "articles_article"."description", "articles_article"."cover_image", "articles_article"."page_title", "articles_article"."category_id", "articles_article"."author_id", "articles_article"."creator_id", "articles_article"."article_type", 0 AS "qs_order" FROM "articles_article" WHERE "articles_article"."id" IN (1, 2, 3)

Ответы [ 2 ]

0 голосов
/ 11 октября 2019

Термин ORDER BY не соответствует ни одному столбцу в наборе результатов

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

Доуглубившись в ответ, давайте посмотрим, что происходит с вашими наборами запросов django под капотом.

Извлечение определенного набора статей и добавление дополнительного поля заказа qs_order, установленного в 0.

articles_by_id = Article.objects.filter(id__in=ids).annotate(qs_order=models.Value(0, models.IntegerField()))

SQL-запрос для вышеупомянутого

Select id, title,....., 0 as qs_order from article where article.id in (Select ....) # whatever you did to get your ids or just a flat list

Получить другой набор статей и включить дополнительное поле заказа qs_order, установленное в 1

articles_by_name = Article.objects.filter(title__icontains='sports').annotate(qs_order=models.Value(1, models.IntegerField()))

SQL-запрос для вышеуказанного

Select id, title, ...1 as qs_order from article where title ilike '%sports%'

Исходный набор запросов и order_by hit_count_generic__hits

Article.objects.order_by('hit_count_generic__hits')

Это фактически выполнитвнутреннее объединение и выборка таблицы счетчиков обращений по столбцам обращений.

Запрос

Select id, title,... from article inner join hitcount on ... order by hits ASC

Объединение

Таким образом, когда вы делаете ваш союз, набор результатов из вышеупомянутых 2 запросов объединяетсяи затем заказал, используя ваш qs_order , а затем ударил ... там, где это не удалось.

Решение

Используйте prefetch_related toполучите свою таблицу количества посещений в начальной фильтрации наборов запросов, чтобы затем использовать столбец совпадений в объединении для упорядочения.

articles_by_id = Article.objects.prefetch_related('hit_count_generic').filter(id__in=ids).annotate(qs_order=models.Value(0, models.IntegerField()))
articles_by_name = Article.objects.prefetch_related('hit_count_generic').filter(title__icontains='sports').annotate(qs_order=models.Value(1, models.IntegerField()))

Теперь, когда у вас есть нужная таблица и ее столбцы в обоих запросах SELECT, вашunion должен работать так, как вы определили.

articles = articles_by_id.union(articles_by_name).order_by('qs_order', 'hit_count_generic__hits')
0 голосов
/ 10 октября 2019

Из Официальная документация Django :

Кроме того, базы данных накладывают ограничения на то, какие операции разрешены в комбинированных запросах. Например, большинство баз данных не допускают LIMIT или OFFSET в комбинированных запросах.

Итак, убедитесь, что ваша база данных позволяет объединять запросы, подобные этому.

...