- 1. urls.py
- 2. views.py
- 3. ArticleManager
- 4. search/index.html
У попередніх статтях ми розглядали, як зробити пошук на сайті на сайті. А саме:
- Впровадження пошуку на сайті
- Розширені параметри пошуку
- Передача списку аргументів в метод order_by для сортування QuerySet
Але що якщо у вас більше, ніж один тип контенту. У вас можуть бути статті, коментарі, форум і повідомлення на форумі. Як тоді бути?
Якщо Ви хочете зробити все самостійно, без застосування сторонніх бібліотек, то тоді потрібно буде зробити пошук по всіх необхідних моделям і об'єднати результат. У мене зроблено точно також на сайті.
urls.py
Вам буде потрібен один View клас, який буде обробляти запит на пошук.
Важливим моментів тут є те, що ми будемо обробляти get запити, щоб користувачі могли ділитися посиланнями з пошуковою видачею.
У файлі 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 = [] # Загальний QuerySet # Шукаємо по всіх моделях 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)) # і об'єднуємо видачу final_set = list(chain(*query_sets)) final_set.sort(key=lambda x: x.pub_date, reverse=True) # виконуємо сортування 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 дуже гнучко і використовуючи качину типізацію писати шаблони для відображення даних, які не залежать від конкретного типу даних, а скоріше, які залежать від інтерфейсу, який підтримують ваші моделі даних.
Також цікаво те, що objects всіх моделей даних, які представлені в даному коді мають один і той же метод search. Даний метод не є стандартним. Для його реалізації необхідно написати власний менеджер моделі і привласнити його полю objects .
ArticleManager
Розглянемо менеджер моделі статей. Успадковуємо його від базового менеджера моделей і визначимо метод search з логікою пошуку. Аналогічним чином потрібно буде прописати даний метод для всіх моделей, в яких буде виконуватися пошук.
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 %}
Спеціально використовую в даному випадку знеособлений параметр object, який не має вказівки на конкретний тип контенту, щоб було ясно, що там може знаходитися будь-якій об'єкт контента. Головне, щоб були реалізовані всі методи у всіх моделей, які застосовуються в даному шаблоні.
Исправьте код в 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
По поводу формы пагинатора, пробовал отключать все дополнительные стили, все также вылезает ошибка. Подумаю еще над этим, главное что есть рабочий вариант.
Спасибо за подсказку.