- 1. urls.py
- 2. views.py
- 3. ArticleManager
- 4. search/index.html
В предыдущих статьях мы рассмотрели, как сделать поиск по сайту на сайте. А именно:
- Реализация поиска по сайту
- Параметры расширенного поиска
- Передача списка аргументов методу order_by для сортировки QuerySet
Но что, если у вас более одного типа контента. Вы можете иметь статьи, комментарии, форум и сообщения на форуме. Как тогда быть?
Если вы хотите сделать все сами, без использования сторонних библиотек, то вам нужно будет сделать поиск по всем нужным моделям и объединить результат. Я сделал точно так же на сайте.
urls.py
Вам понадобится один класс View, который будет обрабатывать поисковый запрос.
Важным моментом здесь является то, что мы будем обрабатывать запросы на получение, чтобы пользователи могли делиться ссылками с результатами поиска.
В файле urls.py пропишем маршрут для поиска
app_name = 'home' urlpatterns = [ path('search/', views.SearchView.as_view(), name='search'), ]
views.py
Допустим, у нас есть несколько типов контента:
- Article
- Comment
- Topic
- Post
Нужно в представлении выполнить поиск по всем видам контента и объединить их в один QuerySet и подготовить к пагинации и выдаче
from itertools import chain from django.shortcuts import render from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage from django.views import View from .models import Article, Comment, Topic, Post class ESearchView(View): template_name = 'search/index.html' def get(self, request, *args, **kwargs): context = {} q = request.GET.get('q') if q: query_sets = [] # Total QuerySet # Searching for all models query_sets.append(Article.objects.search(query=q)) query_sets.append(Comment.objects.search(query=q)) query_sets.append(Topic.objects.search(query=q)) query_sets.append(Post.objects.search(query=q)) # and combine results final_set = list(chain(*query_sets)) final_set.sort(key=lambda x: x.pub_date, reverse=True) # Sorting context['last_question'] = '?q=%s' % q current_page = Paginator(final_set, 10) page = request.GET.get('page') try: context['object_list'] = current_page.page(page) except PageNotAnInteger: context['object_list'] = current_page.page(1) except EmptyPage: context['object_list'] = current_page.page(current_page.num_pages) return render(request=request, template_name=self.template_name, context=context)
Нюанс приведенного выше кода в том, что мы объединяем все модели данных, а также сортируем их по дате. Чтобы это стало возможным, мы будем использовать возможности языка программирования Python, а именно утиную типизацию. Сортировка по дате стала возможной благодаря тому, что во всех моделях данных есть поле даты публикации с одинаковым названием pub_date.
В общем, это очень важно, когда вы пытаетесь назвать поля моделей данных одинаково для разных моделей данных. Это позволяет вам очень гибко разрабатывать сайт Django и использовать утиную типизацию для написания шаблонов для отображения данных, которые не зависят от конкретного типа данных, а скорее зависят от интерфейса, который поддерживает ваши модели данных.
Также интересно то, что объекты всех моделей данных, которые представлены в этом коде, имеют один и тот же метод поиска. Этот метод не является стандартным. Для его реализации нужно написать свой менеджер моделей и назначить его на поле объектов.
ArticleManager
Рассмотрим типовой менеджер статей. Мы наследуем его от базового менеджера моделей и определяем метод поиска с помощью логики поиска. Аналогично нужно прописать этот метод для всех моделей, в которых будет производиться поиск.
from django.db import models from django.db.models import Q class ArticleManager(models.Manager): use_for_related_fields = True def search(self, query=None): qs = self.get_queryset() if query: or_lookup = (Q(title__icontains=query) | Q(content__icontains=query)) qs = qs.filter(or_lookup) return qs
Установка менеджера в модель
class Article(models.Model): objects = ArticleManager()
search/index.html
А для шаблона поиска можно использовать немного измененный шаблон из одной из первых статей по организации поиска.
{% load bootstrap34%} {% block page %} <h1>Поиск</h1> {% if object_list %} {% for object in object_list %} <div> <a href="{{ object.get_absolute_url }}"> <h2>{{ object.itle }}</h2> </a> {{ object.content|safe }} <p><a class="btn btn-default btn-sm" href="{{ object.get_absolute_url }}">Читать далее</a></p> </div> {% endfor %} {% bootstrap_pagination object_list url=last_question %} {% else %} <p>Не найдено публикаций по вашему запросу<br>Попробуйте повторить запрос с другой формулировкой</p> {% endif %} {% endblock %}
Я специально использую в данном случае параметр обезличенного объекта, который не имеет указания на конкретный тип контента, поэтому было понятно, что там может быть любой объект контента. Главное, чтобы все методы были реализованы для всех моделей, которые используются в этом шаблоне.
Исправьте код в views.py, пропущен импорт chain из itertools.
Спасибо! Исправил.
Добрый день, небольшая проблемка с пагинацией, отображается информация только на первой странице при переходе на другую - пустая страница, views, models.Manager, шаблон, все сделал как в данном уроке, подставил только свои значения. Подскажите, в чем может быть проблема?
Спасибо.
Добрый день.
Покажите ваш код... Невозможно понять, где вы допустили ошибку, не видя, что вы делали.
Когда будете добавлять код в сообщение, используйте диалог вставки программного кода, одна из кнопок в тулбаре редактора комментариев. Тогда будет добавлена соответствующая разметка для блоков кода.
Опечатка в статье. Напишите так
И на старуху бывает проруха. Спасибо за вопрос.
Если бы не просмотрел ваш код, то скорее всего не заметил бы опечатку
Спасибо, Евгений, все заработало. Хороший сайт.
Спасибо за отзыв. Не стесняйтесь задавать вопросы. Только у меня убедительная просьба, все вопросы или на форуме сайта, или в комментариях к статьям. Чтобы потом у других пользователей было больше шансов найти ответ. Также на форуме присутствуют и другие опытные специалисты, они вполне могут тоже что-то подсказать, так что шансы получить ответ будут выше.
Здравствуйте, небольшой вопрос,
при добавлении формы пагинатора левая крайняя кнопка назад "<<" постоянно смещена вверх, относительно ряда цифр, не могу ее никак выровнять, в чем может быть проблема?
{% bootstrap_pagination object_list url=last_question %}
Хотел установить пагинатор прописав код, но немного запутался с адресами между страниц.
Подскажите, как правильно прописать адреса.
Спасибо.
У вас есть ещё какие-то кастомные стили, которые могут влиять на "<<", поскольку у меня такой проблемы не наблюдалось.
Ну по идее, это как-то тако должно выглядеть
Добрый день, Евгений,
сработал вот такой вариант:
href="?q={{ question }}&page={{ object_list.previous_page_number }}"
и во views добавил:
context['question'] = q
По поводу формы пагинатора, пробовал отключать все дополнительные стили, все также вылезает ошибка. Подумаю еще над этим, главное что есть рабочий вариант.
Спасибо за подсказку.