@alexreznikoff

Почему не работает сортировка результатов поиска в django-haystack?

Добрый день. Требуется упорядочить поисковую выдачу по дате - сначала новые, потом все более старые новости.
В Джанго есть модель Новостей:
#  news/models.py

class Post(ContentNode, ContentTypeBase):
    title = models.CharField(max_length=255, verbose_name='Заголовок')
    BLOCK_NAME_CHOICES = (
        (0, u'Нет в блоках на главной странице'),
        (1, u'Блок 1'),
        (2, u'Блок 2'),
        (3, u'Блок 3'),
    )
    block_name = models.IntegerField(verbose_name='Блок на главной странице', choices=BLOCK_NAME_CHOICES, default=0)
    block_image = models.ImageField(upload_to='news/', verbose_name='Изображение для блока', blank=True, null=True)
    block_text = models.CharField(max_length=25, verbose_name='Текст для блока (ФРИЗ)', blank=True, null=True)
    block_text_2 = models.CharField(max_length=25, verbose_name='Текст 2-я строка (ФРИЗ)', blank=True, null=True)
    date = models.DateField(verbose_name='Дата', db_index=True)
    update_time = models.DateTimeField(verbose_name='Дата обновления', auto_now=True)
    region = models.ForeignKey(Region, verbose_name='Регион', blank=True, null=True)
    main_image_file = models.ImageField(upload_to='news/', verbose_name='Основное изображение', blank=True, null=True)
    main_image_url = models.CharField(max_length=255, verbose_name='Ссылка на основное изображение', blank=True, null=True)
    gallery = PluploadGalleryField(verbose_name='Дополнительные изображения', blank=True, null=True)
    text = RichTextField(verbose_name='Текст')
    no_persons = models.BooleanField(verbose_name='Нет персон', default=False)    
    mentioned_persons = ManyToManyFieldCheckbox(Person, verbose_name='Упоминаемые персоны', blank=True, null=True, related_name='news_mentions')
    tags = ManyToManyFieldCheckbox(Tags, verbose_name='Теги', blank=True, null=True, related_name='news_set')
    post_to_twitter = models.BooleanField(verbose_name='Опубликовать ссылку в Twitter', default=True)
    posted_to_twitter = models.BooleanField(editable=False, default=False)
    post_to_facebook = PostToFacebookModelField(verbose_name='Опубликовать ссылку в Facebook', default=True)
    posted_to_facebook = models.BooleanField(editable=False, default=False)
    default_template_path = 'news/post.html'
    section_node_id = 7
    objects = ContentTypeManager()
    published_objects = ContentTypePublishedManager()
    objects_with_deleted = models.Manager()

    def __unicode__(self):
        return self.title

    def get_update_time(self):
        return self.update_time

    def get_block_name(self):
        return self.block_name

    def get_template_name(self):
        if settings.IS_MOBILE:
            return 'mobile/news/post.html'
        else:
            return super(Post, self).get_template_name()
    def get_seo_title(self):
        if self.meta_title:
            return self.meta_title
        else:
            return u'{title}'.format(title=self.__unicode__())
    def get_seo_description(self):
        if self.meta_description:
            return self.meta_description
        else:
            return u'{title} - смотрите на телеканале Союз.'.format(title=self.__unicode__())
    def get_seo_keywords(self):
        if self.meta_keywords:
            return self.meta_keywords
        else:
            result = self.get_seo_title()
            signs = ['.',',',':',';']
            for sign in signs:
                result = result.replace(sign, ' ')
            return result
    def generate_human_readable_url(self):
        return clean_friendly_url('/news/' + strip_chars(self.title))
    def get_gallery(self):
        return self.gallery.all()
    def has_video(self):
        """Возвращает True, если у новости есть видео"""
        if '{flvremote}' in self.text or '{youtube}' in self.text:
            return True
        else:
            return False
    def get_main_image_url(self):
        current_site = Site.objects.get_current()
        if self.main_image_url:
            if self.main_image_url.startswith('http://'):
                return self.main_image_url
            else:
                return u'http://{0}{1}'.format(current_site.domain, self.main_image_url)
        elif self.main_image_file:
            return u'http://{0}{1}{2}'.format(current_site.domain, self.main_image_url, self.main_image_file.url)
        else:
            return u'http://{0}{1}'.format(current_site.domain, settings.MAIN_IMAGE_PLACEHOLDER_URL)
    def get_related_news(self, limit=4, offset=0):
        if limit != 0 or offset != 0:
            news = Post.published_objects.filter(date=self.date).exclude(pk=self.pk)[offset:limit+offset]
        else:
            news = Post.published_objects.filter(date=self.date).exclude(pk=self.pk)
        return news
    def get_related_news_count(self):
        return self.get_related_news(limit=0, offset=0).count()
    class Meta:
        verbose_name = 'Новость'
        verbose_name_plural = 'Новости'
        ordering = ('-date',)
        permissions = (
            ('can_edit_foreign_posts', 'Может видеть и редактировать чужие записи'),
        )

Файл описывающий поисковые индексы:
# news/search_indexes.py

from haystack import indexes
from news.models import Post

class NewsIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.EdgeNgramField(document=True, use_template=True)
    date = indexes.DateTimeField(model_attr='date')

    def get_model(self):
        return Post

    def index_queryset(self, using=None):
        return self.get_model().published_objects.all()

Published_manager описан так:
class ContentTypePublishedManager(models.Manager):
    def get_query_set(self):
        qs = super(ContentTypePublishedManager, self).get_query_set().filter(
            is_published=True, is_service=False, is_deleted=False
        )
        return qs

Шаблон templates/search/indexes/news/post_text.txt:
{{ object.title }}
{{ object.category.title }}
{{ object.text }}
{% for tag in object.tags.all %}
{{ tag.title }}
{% endfor %}

Поисковая вьюха:
# search/views.py
from django.template import RequestContext
from django.shortcuts import render_to_response
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

from search.forms import SearchForm
from soyuz_tv.utils import convert_from_en_to_ru_keyboard_layout

def search(request, node):
    per_page = 20
    context = RequestContext(request)
    context['node'] = node
    context['content_object'] = node
    search_form = SearchForm(request.GET)
    if search_form.is_valid():
        search_term = search_form.cleaned_data['q'].replace('"', '')
        context['search_term'] = search_term
        search_results = search_form.search()

        if not search_results.count():
            # Возможно, запрос был введен в неправильной раскладке, пробуем поискать в другой раскладке
            translated_query = convert_from_en_to_ru_keyboard_layout(
                search_term
            )
            translated_query = translated_query
            search_results_trans = search_form.searchqueryset.auto_query(translated_query)
            if search_results_trans.count():
                context['suggested_query'] = translated_query
                search_results = search_results_trans

        paginated_search_results = Paginator(search_results, per_page)
        page = request.GET.get('page')
        try:
            search_results = paginated_search_results.page(page)
        except PageNotAnInteger:
            # If page is not an integer, deliver first page.
            search_results = paginated_search_results.page(1)
        except EmptyPage:
            # If page is out of range (e.g. 9999), deliver last page of results.
            search_results = paginated_search_results.page(paginated_search_results.num_pages)
        context['search_results'] = search_results
        context['paginator'] = paginated_search_results
        context['pagination_url_prefix'] = request.path + '?q=' + search_form.cleaned_data['q']
    template_path = node.get_template_name()
    return render_to_response(template_path, context)

И поисковая форма:
# search/forms.py

from haystack.forms import SearchForm as HaystackSearchForm

class SearchForm(HaystackSearchForm):
    def clean_q(self):
        q = self.cleaned_data.get('q')
        q = q.replace('"', '')
        return q


Несмотря на то, что в модели новостей Post указан порядок выдачи результатов ordering = ('-date'), в результатах поиска новости выдаются в хаотичном порядке, изменение кода в поисковой вьюхе на:
def index_queryset(self, using=None):
        return self.get_model().published_objects.all().order_by('-date')

также порядка выдачи новостей не изменило.
  • Вопрос задан
  • 193 просмотра
Пригласить эксперта
Ответы на вопрос 1
@immaculate
Программист-путешественник
Haystack возвращает объекты из поискового индекса, а не из базы. В том порядке, в котором их вернул используемый бэкенд поиска (например, ElasticSearch). Применяйте order_by к результатам поиска. index_queryset вообще используется только в момент полной индексации. Читайте документацию по пакетам, которые применяете, там такие простые моменты точно рассмотрены.

У вас вообще, с кодом много проблем. Например, это:
return u'{title}'.format(title=self.__unicode__())
эквивалентно:
return self.__unicode__()
или
return unicode(self)

Только последние два варианта читаются проще, и вообще, непонятно, зачем такие сложности. Это из разряда вместо a = 1 писать setattr(__builtin__, 'a', 1).
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы