Встроенная система Django ORM уже предоставляет некоторые фильтры для поиска, но они работают не так, как хотелось бы на деле.
Поэтому мы будем использовать полнотекстовый поисковых движок из модуля django.contrib.postgres
в PostgreSQL, который дает нам отличные инструменты для реализации поиска на сайте.
Создание представления поискового алгоритма
Я буду создавать представление в модуле blog, в файле views/articles.py
Напоминаю, что я использую декомпозицию в своем проекте и храню все по-отдельности, это если вы вдруг не следовали урокам.
modules/blog/views/articles.py
from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank
class ArticleSearchResultView(ListView):
"""
Реализация поиска статей на сайте
"""
model = Article
context_object_name = 'articles'
paginate_by = 10
allow_empty = True
template_name = 'modules/blog/articles/article-list.html'
def get_queryset(self):
query = self.request.GET.get('do')
search_vector = SearchVector('full_description', weight='B') + SearchVector('title', weight='A')
search_query = SearchQuery(query)
return (
self.model.objects.annotate(rank=SearchRank(search_vector, search_query))
.filter(rank__gte=0.3)
.order_by('-rank')
)
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Результаты поиска'
context['do'] = f"do={self.request.GET.get('value')}&"
return context
Пояснения:
- В методе
get_queryset
получаем данные запроса из шаблона. - Создаем вектор SearchVector поиска по полному описанию и названию, присваиваем им символы А и В, что позволяет создать поиск по совпадению для двух полей.
- Пропускаем запрос через SearchQuery, который позволит сортировать результаты по релевантности.
- PostgreSQL предоставляет функцию ранжирования, которая сортирует результаты в зависимости от того, как часто появляется текст запроса и насколько они близки друг к другу.
Далее добавим в urls.py наше представление:
modules/blog/urls.py
from django.urls import path, include
from modules.blog.views import ArticleListView, ArticleDetailView, ArticleByCategoryListView, ArticleCreateView, \
ArticleUpdateView, ArticleDeleteView, CommentCreateView, ArticleByTagListView, ArticleSearchResultView
urlpatterns = [
path('', ArticleListView.as_view(), name='home'),
path('cat/<int:pk>/<str:slug>/', ArticleByCategoryListView.as_view(), name='article-by-cat'),
path('articles/', include([
path('', ArticleListView.as_view(), name='article-list'),
path('create/', ArticleCreateView.as_view(), name='article-create-view'),
path('tags/<str:tag>/', ArticleByTagListView.as_view(), name='article-list-by-tags'),
path('<int:pk>/comments/create/', CommentCreateView.as_view(), name='comment-create-view'),
path('<str:slug>/', ArticleDetailView.as_view(), name='article-detail'),
path('<str:slug>/update/', ArticleUpdateView.as_view(), name='article-update-view'),
path('<str:slug>/delete/', ArticleDeleteView.as_view(), name='article-delete-view'),
])),
path('search/', ArticleSearchResultView.as_view(), name='search'),
]
Перейдем в наш шаблон navbar.html и создадим возможность поиска через форму:
templates/navbar.html
Было
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search">
<input type="search" class="form-control" placeholder="Search..." aria-label="Search">
</form>
Сделали:
<form class="col-12 col-lg-auto mb-3 mb-lg-0 me-lg-3" role="search" method="get" action="{% url 'search' %}">
<input type="search" class="form-control" placeholder="Search..." aria-label="Search" name='do' autocomplete="off" id="search">
</form>
Пояснение:
- Добавил метод в форму (GET)
- Добавил action="{% url 'search' %}
- Добавил в инпут: name='do' autocomplete="off" id="search"
Попробуем наш поиск в деле, в поисковую строку введу "Python" и нажму Enther:

А если введу "Тестовая"

Отлично. Наш поиск работает так, как нам необходимо. Вы можете даже убедиться в этом на моем сайте, на котором сейчас читаете эту статью.
nikollia
Здравствуйте! У меня почему-то не получается реализовать поиск. Все сделала как показано в вашем уроке. При запросе, появляется ошибка, что страница не найдена, что вообще странно ...
Page not found (404) No Article found matching the query Request Method: GET Request URL: http://127.0.0.1:8000/blog/search/?do=django Raised by: blog.views.articles.ArticleDetailView
У меня Mac и бд postgresql устанавливала через Postgres и psycopg2-binary. Может это как-то повлияло на эту ошибку?
Заранее огромное спасибо!
Razilator
nikollia, в воскресенье буду дома, сейчас в МСК, рассмотрю подробно и помогу вам в решении вашей проблемы.
Razilator
nikollia, странно, сделал также как у себя в уроке, и все работает даже на самом примитивном уровне... Я тоже поставил psycopg2-binary, и все также работает.
Может вы не в то место вставили саму ссылку? Или у вас глобально все начинается с префикса /blog/
Просто как таковой, ошибку выдает именно то, что такую страничку вроде найти не может. И попробуйте контроллер с search поднять выше всех url'ов, или же наоборот, вниз самый опустить, и посмотреть, будет ли работать, просто возможно он кушает другое значение и /search/ не воспринимает как отдельный контроллер из-за приоритетов, а кушает его как slug для другого контроллера.
P.S. Посмотрел подробно. Да, точно, у вас скорее всего приоритеты search - ищется как slug какого-то поста, из контроллера ArticleDetailView. Поместите search в самый верх. Ибо наш контроллер с Search называется ArticleSearchResultView, а ловит search - ArticleDetailView, поэтому и выдает ошибку, что страница не найдена. Надо сделать как-то так, только в Вашей интерпретации, Анастасия:
nikollia
Razilator, прошу прощение да долгий ответ. Да, ошибка была именно в расположении. Теперь все работает корректно 👍🏻 Огромное вам спасибо, за помощь!! 😊👍🏻🙏🏻
Razilator
nikollia, не за что, рад был помочь, Анастасия. 😌