Сегодня мы рассмотрим базовые аспекты, связанные с поиском по сайту Django, а также затронем способы улучшить его за счет задействования более продвинутых функций. Полный исходный код материалов этого урока можно найти на GitHub.
Начало работы
Сперва давайте создадим новый Django-проект. Необходимо открыть командную строку и ввести такие команды для установки последней версии.
Убедитесь в том, что у вас установлен Pipenv.
$ pipenv install django==2.2.1 $ pipenv shell $ django-admin startproject citysearch_project . $ python manage.py migrate $ python manage.py runserver
Последовательность действий следующая:
- Сначала устанавливается последняя версия Django с помощью Pipenv.
- Создается проект, который называется citysearch_project.
- Настраивается внутренняя база данных через migrate, а потом запускается локальный веб-сервер с помощью команды runserver.
После того, как вы выполните приведенный выше код и перейдете на страницу http://127.0.0.1:8000/, то увидите приветствие Django, подтверждающее, что все настройки выполнены правильно. Локальный сервер не выражает все моменты реальной работы сайта на сервере. Ознакомиться со списком хостингов можно на сайте http://hostinghub.ru/top/vds/.
Создаем приложение Cities в Django
Теперь давайте создадим одну программу, которая называется cities, в которой будет храниться список названий городов.
Мы специально откажемся от использования сложных команд и комбинаций.
Для начала необходимо воспользоваться комбинацией клавиш Ctrl + C, чтобы остановить локальный сервер. Затем воспользуйтесь командой startapp, с помощью которой будет создаваться наше новое приложение.
$ python manage.py startapp cities
После этого нужно обнровить INSTALLED_APPS внутри нашего файла settings.py. Таким образом вы сообщаете Django о новом приложении.
# citysearch_project/settings.py INSTALLED_APPS = [ ... 'cities.apps.CitiesConfig', # new ]
Теперь перейдем к моделям. Мы нашей единственной модели дадим название City. В ней будет два поля: name и state. Поскольку админка рассматриваемого нами фреймворка будет по умолчанию изменять имя приложения во множественном числе на Citys, то также необходимо выполнить настройку verbose_name_plural. И наконец, настроим __str__ для того, чтобы показывать имя города.
# cities/models.py from django.db import models class City(models.Model): name = models.CharField(max_length=255) state = models.CharField(max_length=255) class Meta: verbose_name_plural = "cities" def __str__(self): return self.name
Прекрасно, теперь у нас все настроено. Мы можем создать файл миграции для этого изменения, а потом воспользоваться migrate, чтобы добавить его в нашу базу данных.
$ python manage.py makemigrations cities $ python manage.py migrate
Существует несколько вариантов заполнения базы данных. Правда, самый простой способ — через админку. Для начала необходимо создать учетную запись суперпользователя, через которую становится возможным вход в админку.
$ python manage.py createsuperuser
Теперь нам необходимо обновить cities/admin.py, чтобы отобразить наше приложение внутри админки.
# cities/admin.py from django.contrib import admin from .models import City class CityAdmin(admin.ModelAdmin): list_display = ("name", "state",) admin.site.register(City, CityAdmin)
Далее необходимо осуществить повторный запуск сервера, воспользоавшись командой python manage.py runserver, после чего откройте админку по адресу, указанному на скриншоте. И, в конце концов, зайдите в аккаунт суперпользователя.
Нажмите на раздел cities и добавьте несколько записей. Здесь видно четыре примера.
Домашняя страница и страница выдачи поиска Django
Мы имеем заполненную базу данных. Тем не менее, все еще есть ряд шагов, которые необходимо выполнить перед тем, как станет возможно ее отображение на сайте. В конечном итоге, нам необходимо иметь лишь домашнюю страницу и страницу выдачи поиска.
Каждой странице нужен надлежащий вид, url, шаблон. Порядок, в котором будет создаваться каждая страница, в нашем случае не имеет особого значения. Главное, чтобы на сайте присутствовали все необходимые компоненты.
В целом, рекомендуем начинать с URL, а потом добавить views. А в конце концов, создать шаблоны. Это – оптимальный вариант действий.
Сперва необходимо добавить путь URL для нашего приложения, это можно сделать, импортировав include и настроив путь к нему.
# citysearch_project/urls.py from django.contrib import admin from django.urls import path, include # new urlpatterns = [ path('admin/', admin.site.urls), path('', include('cities.urls')), # new ]
Затем, нам нужен urls.py внутри приложения cities. Правда, Django не предусматривает возможности создавать такой для нас по команде startapp. Но нет поводов волноваться, поскольку всегда его можно создать в командной строке. Для начала воспользуемся комбинацией клавиш Ctrl + C, чтобы остановить сервер (если он работает состоянием на этот момент у вас).
$ touch cities/urls.py
Внутри этого файла необходимо импортировать еще не созданные представления для каждой HomePageView, SearchResultsView и указать путь к каждому из них. Учтите то, что нами указывается опциональное название URL для каждого из них.
Вот, как будет все приблизительно выглядеть.
# cities/urls.py from django.urls import path from .views import HomePageView, SearchResultsView urlpatterns = [ path('search/', SearchResultsView.as_view(), name='search_results'), path('', HomePageView.as_view(), name='home'), ]
В третьих, нам надо выполнить настройку наших двух представлений (views). Домашняя страница будет простым шаблоном с итоговой поисковой строкой. Для Django прекрасно подходит TemplateView, чтобы добиться этой задачи. Страница поисковой выдачи упорядочит требуемые результаты, что прекрасно ложится под ListView.
# cities/views.py from django.views.generic import TemplateView, ListView from .models import City class HomePageView(TemplateView): template_name = 'home.html' class SearchResultsView(ListView): model = City template_name = 'search_results.html'
На последнем этапе нам необходимо работать с нашими шаблонами. Мы можем добавить шаблоны внутри нашего приложения cities. Правда, есть и более легкий вариант действий. Если говорить более конкретно, то можно создать папку проектных шаблонов.
Итак, давайте создадим папку с шаблонами, а потом два шаблона home.html, search_results.html.
$ mkdir templates $ touch templates/home.html $ touch templates/search_results.html
Учтите и то, что нам также понадобится внести изменения в наш файл settings.py, чтобы указать Django на проектную папку с шаблонами. В разделе TEMPLATES это также можно будет найти.
# citysearch_project/settings.py TEMPLATES = [ { ... 'DIRS': [os.path.join(BASE_DIR, 'templates')], # new ... } ]
В итоге, домашняя страница покажет лишь заголовок.
<!-- templates/home.html --> <h1>HomePage</h1>
Если запустить веб-сервер повторно, то мы увидим домашнюю страницу.
Теперь для страницы выдачи, которая будет проходить через object_list, будет возвращено имя из контекстного объекта ListView. После этого мы будем отображать имя и состояние для каждой записи из базы данных.
<!-- templates/search_results.html --> <h1>Search Results</h1> <ul> {% for city in object_list %} <li> {{ city.name }}, {{ city.state }} </li> {% endfor %} </ul>
Все готово! Наша страница поисковой выдачи доступна на http://127.0.0.1:8000/search/.
Формы и наборы запросов в Django
В результате, базовая реализация поиска сводится к форме, передающей пользовательский запрос. То есть, непосредственно сам поиск. А потом переходит к набору инструментов для фиксации результатов, полученных на основе ключевых слов, введенных пользователем.
Мы можем начать с какого-угодно варианта. Но давайте начнем с настройки фильтрации, а потом перейдем к форме.
Базовая фильтрация запросов в Django
В Django, QuerySet нужен для того, чтобы фильтровать выдачу из модели базы данных. На данный момент, наша модель City выводит все данные, которые в ней содержатся. Наша задача – ограничить страницу поисковой выдачи для фильтрации результатов, которые были выведены, основываясь на поисковом запросе от пользователя.
Существует несколько способов настройки набора запросов. На самом деле реально фильтровать через сам менеджер моделей. Правда, чтобы упростить действия, вы можете добавить фильтр только в одну строку. Давайте попробуем это сделать.
Здесь мы обновляем метод queryset из ListView, а фильтр добавляется таким образом, что возвращается лишь город под названием Бостон. Следовательно, она будет заменена переменной, представляющей запрос пользователя.
# cities/views.py class SearchResultsView(ListView): model = City template_name = 'search_results.html' queryset = City.objects.filter(name__icontains='Boston') # новый
А теперь давайте попробуем обновить страницу поисковой выдачи для того, чтобы убедиться в том, что отображается исключительно Бостон.
Теперь можно выполнить настройку queryset путем переопределения метода get_queryset() для того, чтобы изменить список выданных городов.
# cities/views.py ... class SearchResultsView(ListView): model = City template_name = 'search_results.html' def get_queryset(self): # новый return City.objects.filter(name__icontains='Boston')
Этот вариант будет более гибким.
Объекты Q в Django
Почему хорошо использовать filter()? Потому что он позволяет связать фильтры вместе. Тем не менее, в некоторых случаях могут понадобиться более сложные запросы, такие как ИЛИ (OR). В этом случае можно воспользоваться объемами Q.
Вот пример того, как они используются.
# cities/views.py from django.db.models import Q # новый ... class SearchResultsView(ListView): model = City template_name = 'search_results.html' def get_queryset(self): # новый return City.objects.filter( Q(name__icontains='Boston') | Q(state__icontains='NY') )
И такой результат мы получаем после того, как страница поисковой выдачи будет обновлена.
Формы для ввода данных на сайте Django
Фактически схема работы с формами простая. Это совокупность POST и GET запросов. Собственно, один из этих методов и нужно использовать. Давайте попробуем разобраться, как это реализуется на практике.
Поисковая форма для Django-проекта
Давайте попробуем создать поисковую форму для Django-сайта. Вот так она будет выглядеть в конечном итоге. Всего пара строк кода, не так ли?
<!-- templates/home.html --> <h1>HomePage</h1> <form action="{% url 'search_results' %}" method="get"> <input name="q" type="text" placeholder="Search..."> </form>
Для формы, параметр action определяет, куда пользователь должен быть направлен после того, как кнопка поиска будет нажата. Используется адрес страницы поисковой выдачи. После этого определяется использование GET в качестве запроса, через который все это реализуется.
После всего этого необходимо взять пользовательский поисковый запрос и передать его дальше в логику приложения.
# cities/views.py ... class SearchResultsView(ListView): model = City template_name = 'search_results.html' def get_queryset(self): # новый query = self.request.GET.get('q') object_list = City.objects.filter( Q(name__icontains=query) | Q(state__icontains=query) ) return object_list
Все, результат есть. Отлично.
Теперь давайте посмотрим на реальном примере, как это все будет работать. Давайте попробуем ввести в строку поиска запрос «San Diego» и посмотрим на то, что получилось в итоге.
Что дальше?
На этом данный урок подошел к концу. Конечно, это только основы поиска в Django. Все намного сложнее, на самом деле. Возможно, вы желаете добавить кнопку в поисковую форму, которая также может нажиматься, как и Enter? Или желаете добавить какую-нибудь валидацию формы?
Кроме фильтрации с использованием AND, OR, имеются и другие факторы. Например, необходимо учитывать релевантность, если поисковик должен быть хотя бы немного приближен к знаменитому Google.