Изучаем Python. Программирование игр, визуализация данных, веб-приложения

Мэтиз Эрик

Проект 3. Веб-приложения

 

18. Знакомство с Django

Современные веб-сайты в действительности представляют собой многофункцио­нальные приложения, достаточно близкие к полноценным приложениям для настольных систем. Python содержит богатый набор инструментов для построения веб-приложений. В этой главе вы научитесь использовать Django (http://djangoproject.com/) для построения проекта Learning Log — сетевой журнальной системы для отслеживания информации, полученной вами по определенной теме.

Мы напишем спецификацию для этого проекта, а затем определим модели для данных, с которыми будет работать приложение. Мы воспользуемся административной системой Django для ввода некоторых начальных данных, а затем научимся писать представления и шаблоны, на базе которых Django будет строить страницы нашего сайта.

Django представляет собой веб-инфраструктуру — набор инструментов для построения интерактивных веб-сайтов. Django может реагировать на запросы страниц, упрощает чтение и запись информации в базы данных, управление пользователями и многие другие операции. В главах 19 и 20 мы доработаем проект Learning Log, а затем развернем его на сервере, чтобы вы (и ваши друзья) могли использовать их.

 

Подготовка к созданию проекта

В начале работы над проектом необходимо описать проект в спецификации. Затем вы создадите виртуальную среду для построения проекта.

 

Написание спецификации

В полной спецификации описываются цели проекта, его функциональность, а также внешний вид и интерфейс пользователя. Как и любой хороший проект или бизнес-план, спецификация должна сосредоточиться на самых важных аспектах и обеспечивать планомерную разработку проекта. Здесь мы не будем писать полную спецификацию, а сформулируем несколько четких целей, которые будут задавать направление процесса разработки. Вот как выглядит спецификация:

Мы напишем веб-приложение с именем Learning Log, при помощи которого пользователь сможет вести журнал интересующих его тем и создавать записи в журнале во время изучения каждой темы. Домашняя страница Learning Log содержит описание сайта и приглашает пользователя зарегистрироваться либо ввести свои учетные данные. После успешного входа пользователь получает возможность создавать новые темы, добавлять новые записи, читать и редактировать существу­ющие записи.

Во время изучения нового материала бывает полезно вести журнал того, что вы узнали, — записи пригодятся для контроля и возвращения к необходимой информации. Хорошее приложение повышает эффективность этого процесса.

 

Создание виртуальной среды

Для работы с Django необходимо сначала создать виртуальную среду для работы. Виртуальная среда представляет собой подраздел системы, в котором вы можете устанавливать пакеты в изоляции от всех остальных пакетов Python. Отделение библиотек одного проекта от других проектов принесет пользу при развертывании Learning Log на сервере в главе 20.

Создайте для проекта новый каталог с именем learning_log, перейдите в этот ­каталог в терминальном режиме и создайте виртуальную среду. Если вы работаете в Python 3, то сможете создать виртуальную среду следующей командой:

learning_log$ python -m venv ll_env

learning_log$

Команда запускает модуль venv и использует его для создания виртуальной среды с именем ll_env. Если этот способ сработал, переходите к разделу «Активизация виртуальной среды» на с. 382. Если что-то не получилось, прочитайте следующий раздел — «Установка virtualenv».

 

Установка virtualenv

Если вы используете более раннюю версию Python или ваша система не настроена для правильного использования модуля venv, установите пакет virtualenv. Установка virtualenv выполняется следующей командой:

$ pip install --user virtualenv

Возможно, вам придется использовать слегка измененную версию этой команды. (Если вы еще не использовали pip, обратитесь к разделу «Установка пакетов Python с использованием pip» на с. 227.)

Примечание

Если вы используете Linux, но и этот способ не сработал, установите virtualenv при помощи менеджера пакетов своей системы. Например, в Ubuntu для установки virtualenv используется команда sudo apt-get install python-virtualenv.

Перейдите в каталог learning_log в терминальном окне и создайте виртуальную среду следующей командой:

learning_log$ virtualenv ll_env

New python executable in ll_env/bin/python

Installing setuptools, pip...done.

learning_log$

Примечание

Если в вашей системе установлено несколько версий Python, укажите версию, которая должна использоваться virtualenv. Например, команда virtualenv ll_env --python=python3 создаст виртуальную среду, которая использует Python 3.

 

Активизация виртуальной среды

После того как виртуальная среда будет создана, ее необходимо активизировать следующей командой:

learning_log$ source ll_env/bin/activate

(1) (ll_env)learning_log$

Команда запускает сценарий activate из каталога ll_env/bin. Когда среда активизируется, ее имя выводится в круглых скобках (1) ; теперь вы можете устанавливать пакеты в среде и использовать те пакеты, что были установлены ранее. Пакеты, установленные в ll_env, будут доступны только в то время, пока среда остается активной.

Примечание

Если вы работаете в системе Windows, используйте команду ll_env\Scripts\activate (без слова source) для активизации виртуальной среды.

Чтобы завершить использование виртуальной среды, введите команду deactivate:

(ll_env)learning_log$ deactivate

learning_log$

Среда также становится неактивной при закрытии терминального окна, в котором она работает.

 

Установка Django

После того как вы создали свою виртуальную среду и активизировали ее, установите Django:

(ll_env)learning_log$ pip install Django

Installing collected packages: Django

Successfully installed Django

Cleaning up...

(ll_env)learning_log$

Так как вы работаете в виртуальной среде, эта команда выглядит одинаково во всех системах. Использовать флаг --user не нужно, как и использовать более длинные команды вида python -m pip install имя_пакета.

Помните, что с Django можно работать только в то время, пока среда остается активной.

 

Создание проекта в Django

Не выходя из активной виртуальной среды (пока ll_env выводится в круглых скобках), введите следующие команды для создания нового проекта:

(1) (ll_env)learning_log$ django-admin.py startproject learning_log .

(2)(ll_env)learning_log$ ls

learning_log ll_env manage.py

(3)(ll_env)learning_log$ ls learning_log

__init__.py settings.py urls.py wsgi.py

Команда (1) приказывает Django создать новый проект с именем learning_log. Точка в конце команды создает новый проект со структурой каталогов, которая упрощает развертывание приложения на сервере после завершения разработки.

Примечание

Не забывайте про точку, иначе у вас могут возникнуть проблемы с конфигурацией при развертывании приложения. А если вы все же забыли, удалите созданные файлы и папки (кроме ll_env) и снова выполните команду.

Команда ls (dir в Windows) (2) показывает, что Django создает новый каталог с именем learning_log. Также создается файл manage.py — короткая программа, которая получает команды и передает их соответствующей части Django для выполнения. Мы используем эти команды для управления такими задачами, как работа с базами данных и запуск серверов.

В каталоге learning_log находятся четыре файла (3), важнейшими из которых являются файлы settings.py, urls.py и wsgi.py. Файл settings.py определяет то, как Django взаимодействует с вашей системой и управляет вашим проектом. Мы изменим некоторые из существующих настроек и добавим несколько новых настроек в ходе разработки проекта. Файл urls.py сообщает Django, какие страницы следует строить в ответ на запросы браузера. Файл wsgi.py помогает Django предоставлять созданные файлы (имя файла является сокращением от «Web Server Gateway Interface»).

 

Создание базы данных

Так как Django хранит бульшую часть информации, относящейся к проекту, в базе данных, необходимо создать базу данных, с которой Django сможет работать. Чтобы создать базу данных для проекта Learning Log, введите следующую команду (все еще не покидая активной среды):

(ll_env)learning_log$ python manage.py migrate

(1) Operations to perform:

Synchronize unmigrated apps: messages, staticfiles

Apply all migrations: contenttypes, sessions, auth, admin

...

Applying sessions.0001_initial... OK

(2)(ll_env)learning_log$ ls

db.sqlite3 learning_log ll_env manage.py

Каждое изменение базы данных называется миграцией. Первое выполнение команды migrate приказывает Django проверить, что база данных соответствует текущему состоянию проекта. Когда мы впервые выполняем эту команду в новом проекте с использованием SQLite (вскоре мы расскажем о SQLite более подробно), Django создает новую базу данных за нас. В точке (1) Django сообщает о создании таблиц базы данных, необходимых для хранения информации, используемой в проекте (Synchronize unmigrated apps), а затем проверяет, что структура базы данных соответствует текущему коду (Apply all migrations).

Выполнение команды ls показывает, что Django создает другой файл с именем db.sqlite3 (2). SQLite — база данных, работающая с одним файлом; она идеально подходит для написания простых приложений, потому что вам не нужно особенно следить за управлением базой данных.

 

Просмотр проекта

Убедимся в том, что проект был создан правильно. Введите команду runserver:

(ll_env)learning_log$ python manage.py runserver

Performing system checks...

(1) System check identified no issues (0 silenced).

July 15, 2015 - 06:23:51

(2)Django version 1.8.4, using settings 'learning_log.settings'

(3)Starting development server at http://127.0.0.1:8000/

Quit the server with CONTROL-C.

Django запускает сервер, чтобы вы могли просмотреть проект в своей системе и проверить, как он работает. Когда вы запрашиваете страницу, вводя URL в браузере, сервер Django отвечает на запрос; для этого он строит соответствующую страницу и отправляет страницу браузеру.

В точке (1) Django проверяет правильность созданного проекта; в точке (2) выводится версия Django и имя используемого файла настроек; в точке (3) возвращается URL-адрес, по которому доступен проект. URL http://127.0.0.1:8000/ означает, что проект ведет прослушивание запросов на порте 8000 локального хоста (localhost), то есть вашего компьютера. Термином «локальный хост» обозначается сервер, который обрабатывает только запросы вашей системы; он не позволяет никому другому просмотреть разрабатываемые страницы.

Теперь откройте браузер и введите URL http://localhost:8000/ — или http://127.0.0.1:8000/, если первый адрес не работает. Вы увидите нечто похожее на рис. 18.1 — страницу, которую создает Django, чтобы сообщить вам, что все пока работает правильно. Пока не завершайте работу сервера (но, когда вы захотите прервать ее, это можно сделать нажатием клавиш Ctrl+C).

Примечание

Если вы получаете сообщение об ошибке «Порт уже используется», прикажите Django использовать другой порт; для этого введите команду python manage.py runserver 8001 и продолжайте перебирать номера портов по возрастанию, пока не найдете открытый порт.

Упражнения

18-1. Новые проекты: чтобы лучше понять, что делает Django, постройте пару пустых проектов и посмотрите, что будет создано. Создайте новый каталог с простым именем типа InstaBook или FaceGram (за пределами каталога learning_log), перейдите в этот каталог в терминальном окне и создайте виртуальную среду. Установите Django и выполните ­команду django-admin.py startproject instabook. (обратите внимание на точку в конце ­команды).

Просмотрите файлы и каталоги, созданные командой, и сравните их с файлами и каталогами Learning Log. Проделайте это несколько раз, пока не начнете хорошо понимать, что именно создает Django при создании нового проекта, а затем удалите каталоги проектов.

Рис. 18.1. Пока все работает правильно

 

Начало работы над приложением

Проект Django представляет собой группу отдельных приложений, совместная работа которых обеспечивает работу проекта в целом. Пока мы создадим одно приложение, которое будет выполнять бульшую часть работы в нашем проекте. Другое приложение для управления учетными записями пользователей будет добавлено в главе 19.

К этому моменту команда runserver должна продолжать работу в терминальном окне, которое вы открыли ранее. Откройте новое терминальное окно (или вкладку) и перейдите в каталог, содержащий manage.py. Активизируйте виртуальную среду и выполните команду startapp:

learning_log$ source ll_env/bin/activate

(ll_env)learning_log$ python manage.py startapp learning_logs

(1) (ll_env)learning_log$ ls

db.sqlite3 learning_log learning_logs ll_env manage.py

(2)(ll_env)learning_log$ ls learning_logs/

admin.py __init__.py migrations models.py tests.py views.py

Команда startapp имя_приложения приказывает Django создать инфраструктуру, необходимую для построения приложения. Заглянув сейчас в каталог проекта, вы найдете в нем новый подкаталог с именем learning_logs (1) . Откройте этот каталог, чтобы увидеть, какие файлы были созданы Django (2). Самые важные файлы в этом каталоге — models.py, admin.py и views.py.

Мы воспользуемся файлом models.py для определения данных, которыми нужно управлять в нашем приложении. К файлам admin.py и views.py мы вернемся позднее.

 

Определение моделей

Ненадолго задумаемся, какие данные нам понадобятся. Каждый пользователь создает набор тем в своем журнале. Каждая запись, которую он сделает, будет привязана к определенной теме, а записи будут выводиться в текстовом виде. Также необходимо хранить временну?ю метку каждой записи, чтобы пользователь знал, когда эта запись была создана.

Откройте файл models.py и просмотрите его текущее содержимое:

models.py

from django.db import models

# Создайте здесь свои модели.

Модуль с именем models импортируется автоматически, и нам предлагается создать свои модели. Модель сообщает Django, как работать с данными, которые будут храниться в приложении. С точки зрения кода модель представляет собой обычный класс; она содержит атрибуты и методы, как и все остальные классы, рассматривавшиеся нами ранее. Вот как выглядит модель тем обсуждения, которые будут сохраняться пользователями:

from django.db import models

class Topic(models.Model):

. ."""Тема, которую изучает пользователь"""

(1) . .text = models.CharField(max_length=200)

(2) . .date_added = models.DateTimeField(auto_now_add=True)

(3) . .def __str__(self):

. . . ."""Возвращает строковое представление модели."""

. . . .return self.text

Мы создали класс с именем Topic, наследующий от Model — родительского класса, включенного в Django и определяющего базовую функциональность модели. Класс Topic содержит всего два атрибута: text и date_added.

Атрибут text содержит данные CharField — блок данных, состоящий из символов, то есть текст (1) . Атрибуты CharField могут использоваться для хранения небольших объемов текста: имен, заголовков, названий городов и т.д. При определении атрибута CharField необходимо сообщить Django, сколько места нужно зарезервировать для него в базе данных. В данном случае задается максимальная длина max_length, равная 200 символам; этого должно быть достаточно для хранения большинства имен тем.

Атрибут date_added содержит данные DateTimeField — блок данных для хранения даты и времени (2). Аргумент auto_add_now=True приказывает Django автоматически присвоить этому атрибуту текущую дату и время каждый раз, когда пользователь создает новую тему.

Примечание

Полный список всех полей, которые могут использоваться в модели, приведены в документе Django Model Field Reference по адресу https://docs.djangoproject.com/en/1.8/ref/models/fields/. Возможно, вся эта информация вам сейчас не понадобится, но она будет в высшей степени полезной, когда вы начнете разрабатывать собственные приложения.

Необходимо сообщить Django, какой атрибут должен использоваться по умолчанию при вводе информации о теме. Django вызывает метод __str__() для вывода простого представления модели. Мы написали реализацию __str__(), которая возвращает строку, хранящуюся в атрибуте text (3).

Примечание

Если вы используете Python 2.7, метод __str__() должен называться __unicode__(). Тело метода остается неизменным.

 

Активизация моделей

Чтобы использовать модели, необходимо приказать Django включить приложение в общий проект. Откройте файл settings.py (из каталога learning_log/learning_log) и найдите в нем раздел, который сообщает Django, какие приложения установлены в проекте:

settings.py

...

INSTALLED_APPS = (

. .'django.contrib.admin',

. .'django.contrib.auth',

. .'django.contrib.contenttypes',

. .'django.contrib.sessions',

. .'django.contrib.messages',

. .'django.contrib.staticfiles',

)

...

Это обычный кортеж, который сообщает Django, какие приложения образуют проект. Добавьте наше приложение в этот кортеж; измените содержимое INSTALLED_APPS, чтобы оно выглядело так:

...

INSTALLED_APPS = (

...

'django.contrib.staticfiles',

. .

. .# Мои приложения

. .'learning_logs',

)

...

Группировка приложений в проекте упрощает управление ими по мере того, как проект растет, а количество приложений увеличивается. Здесь мы создаем раздел, который пока содержит только приложение learning_logs.

Затем необходимо приказать Django изменить базу данных для хранения информации, относящейся к модели Topic. В терминальном окне введите следующую команду:

(ll_env)learning_log$ python manage.py makemigrations learning_logs

Migrations for 'learning_logs':

0001_initial.py:

. .- Create model Topic

(ll_env)learning_log$

По команде makemigrations Django определяет, как изменить базу данных для хранения информации, связанной с новыми моделями. Из результатов видно, что Django создает файл миграции с именем 0001_initial.py. Эта миграция создает в базе данных таблицу для модели Topic.

Теперь применим миграцию для автоматического изменения базы данных:

(ll_env)learning_log$ python manage.py migrate

...

Running migrations:

Rendering model states... DONE

(1) Applying learning_logs.0001_initial... OK

Бульшая часть вывода этой команды совпадает с выводом, полученным при первом выполнении команды migrate. Обратить внимание следует на строку (1) ; здесь Django подтверждает, что применение миграции для learning_logs прошло успешно.

Каждый раз, когда вы захотите изменить данные, которыми управляет Learning Log, выполните эти три действия: внесите изменения в models.py, вызовите makemigrations для learning_logs и прикажите Django выполнить миграцию проекта (migrate).

 

Административный сайт Django

Django позволяет легко работать с моделями, определенными для приложения, через административный сайт. Этот сайт используется администраторами сайта, а не рядовыми пользователями. В этом разделе мы создадим административный сайт и используем его для добавления некоторых тем через модель Topic.

 

Создание суперпользователя

Django позволяет создать пользователя, обладающего полным набором привилегий на сайте; такой пользователь называется суперпользователем. Привилегии управляют действиями, которые разрешено выполнять пользователю. На самом жестком уровне привилегий пользователь может только читать общедоступную информацию на сайте. Зарегистрированным пользователям обычно предоставляется привилегия чтения своих приватных данных, а также избранной информации, доступной только для участников сообщества. Для эффективного администрирования веб-приложения владельцу сайта обычно должна быть доступна вся информация, хранящаяся на сайте. Хороший администратор внимательно относится к конфиденциальной информации пользователя, потому что пользователи доверяют тем приложениям, с которыми они работают.

Чтобы создать суперпользователя в Django, введите следующую команду и ответьте на запросы:

(ll_env)learning_log$ python manage.py createsuperuser

(1) Username (leave blank to use 'ehmatthes'): ll_admin

(2)Email address:

(3)Password:

Password (again):

Superuser created successfully.

(ll_env)learning_log$

При получении команды createsuperuser Django предлагает ввести имя пользователя, который является суперпользователем (1) . Здесь мы вводим имя ll_admin, но вы можете ввести любое имя на свое усмотрение. Также можно ввести адрес электронной почты или оставить это поле пустым (2). После этого следует дважды ввести пароль (3).

Примечание

Часть конфиденциальной информации может быть скрыта от администраторов сайта. Например, Django на самом деле не сохраняет введенный пароль; вместо этого сохраняется хеш — специальная строка, построенная на основе пароля. И когда в будущем вы вводите пароль, Django снова хеширует введенные данные и сравнивает результат с хранимым хешем. Если два хеша совпадают, то проверка пройдена. Если же хакер в результате атаки получит доступ к базе данных сайта, он сможет прочитать только хранящийся в базе хеш, но не пароли. При правильной настройке сайта восстановить исходные пароли из хешей почти невозможно.

 

Регистрация модели на административном сайте

Django добавляет некоторые модели (например, User и Group) на административный сайт автоматически, но модели, которые мы создали, придется регистрировать вручную.

При запуске приложения learning_logs Django создает файл admin.py в одном каталоге с models.py:

admin.py

from django.contrib import admin

# Зарегистрируйте здесь ваши модели.

Чтобы зарегистрировать Topic на административном сайте, введите следующую команду:

from django.contrib import admin

(1) from learning_logs.models import Topic

(2)admin.site.register(Topic)

Этот код импортирует модель Topic (1) , после чего использует вызов admin.site.register() (2), регистрирующий модель для управления через административный сайт.

Теперь используйте учетную запись суперпользователя для входа на административный сайт. Введите адрес http://localhost:8000/admin/, введите имя пользователя и пароль для только что созданного суперпользователя, и вы увидите экран наподобие изображенного на рис. 18.2. На этой странице можно добавлять новых пользователей и группы, а также вносить изменения в уже существующие настройки. Помимо этого можно работать с данными, связанными с только что определенной моделью Topic.

Рис. 18.2. Административный сайт с включением модели Topic

Примечание

Если в браузере появляется сообщение о недоступности веб-страницы, убедитесь в том, что сервер Django работает в терминальном окне. Если сервер не работает, активизируйте виртуальную среду и снова введите команду python manage.py runserver.

 

Добавление тем

Когда модель Topic зарегистрирована на административном сайте, добавим первую тему. Щелкните на ссылке Topics, чтобы перейти к странице Topics; страница практически пуста, потому что еще нет ни одной темы для выполнения операций. Щелкните на ссылке Add; открывается форма для добавления новой темы. Введите в первом поле текст Chess и щелкните на ссылке Save. Вы возвращаетесь к административной странице Topics, на которой появляется только что созданная тема.

Создадим вторую тему, чтобы у вас было больше данных для работы. Снова щелк­ните на ссылке Add и создайте вторую тему Rock Climbing. Ссылка Save снова возвращает вас к основной странице Topics, где отображаются обе темы, Chess и Rock Climbing.

 

Определение модели Entry

Чтобы сохранить информацию о том, что вы узнали по этим двум темам, необхо­димо определить модель для записей, которые пользователь делает в своих журналах. Каждая запись должна ассоциироваться с конкретной темой. Такое отношение называется отношением «многие-к-одному», поскольку многие записи могут быть связаны с одной темой.

Код модели Entry выглядит так:

models.py

from django.db import models

class Topic(models.Model):

. ....

(1) class Entry(models.Model):

. ."""Информация, изученная пользователем по теме"""

(2) . .topic = models.ForeignKey(Topic)

(3) . .text = models.TextField()

. .date_added = models.DateTimeField(auto_now_add=True)

. .

(4) . .class Meta:

. . . .verbose_name_plural = 'entries'

. .def __str__(self):

. . . ."""Возвращает строковое представление модели."""

(5) . . . .return self.text[:50] + "..."

Класс Entry наследует от базового класса Model, как и рассмотренный ранее класс Topic (1) . Первый атрибут, topic, является экземпляром ForeignKey (2). Термин «внешний ключ» (foreign key) происходит из теории баз данных; внешний ключ содержит ссылку на другую запись в базе данных. Таким образом каждая запись связывается с конкретной темой. Каждой теме при создании присваивается ключ, или идентификатор. Если потребуется установить связь между двумя записями данных, Django использует ключ, связанный с каждым блоком информации. Вскоре мы используем такие связи для получения всех записей, связанных с заданной темой.

Затем идет атрибут с именем text, который является экземпляром TextField (3). Полю такого типа ограничение размера не требуется, потому что размер отдельных записей не ограничивается. Атрибут date_added позволяет отображать записи в порядке их создания и снабдить каждую запись временной меткой.

В точке (4) класс Meta вкладывается в класс Entry. Класс Meta хранит дополнительную информацию по управлению моделью; в данном случае он позволяет задать специальный атрибут, который приказывает Django использовать форму множественного числа Entries при обращении более чем к одной записи. (Без этого Django будет использовать неправильную форму Entrys.) Наконец, метод __str__() сообщает Django, какая информация должна отображаться при обращении к отдельным записям. Так как запись может быть достаточно длинным блоком текста, мы приказываем Django выводить только первые 50 символов (5). Также добавляется многоточие — признак вывода неполного текста.

 

Миграция модели Entry

Так как мы добавили новую модель, миграцию базы данных необходимо провести снова. Вскоре вы привыкнете к этому процессу: вы изменяете models.py, выполняете команду python manage.py makemigrations имя_приложения, а затем команду python manage.py migrate.

Проведите миграцию базы данных и проверьте вывод:

(ll_env)learning_log$ python manage.py makemigrations learning_logs

Migrations for 'learning_logs':

(1) 0002_entry.py:

. .- Create model Entry

(ll_env)learning_log$ python manage.py migrate

Operations to perform:

...

(2) Applying learning_logs.0002_entry... OK

Команда генерирует новую миграцию с именем 0002_entry.py, которая сообщает Django, как изменить базу данных для хранения информации, связанной с моделью Entry (1) . При выдаче команды migrate Django подтверждает, что применение миграции прошло успешно (2).

 

Регистрация Entry на административном сайте

Модель Entry тоже необходимо зарегистрировать. Файл admin.py должен выглядеть так:

admin.py

from django.contrib import admin

from learning_logs.models import Topic, Entry

admin.site.register(Topic)

admin.site.register(Entry)

Вернитесь на страницу http://localhost/admin/, и вы увидите раздел Entries в категории learning_logs. Щелкните на ссылке Add для Entries или щелкните на Entries и выберите вариант Add. На экране должен появиться раскрывающийся список для выбора темы, для которой создается запись, и текстовое поле для ввода записи. Выберите в раскрывающемся списке вариант Chess и добавьте запись. При выборе ссылки Save вы вернетесь к основной административной странице. Здесь проявляются преимущества использования формата text[:50] в качестве строкового представления каждой записи; работать с несколькими записями в административном интерфейсе намного удобнее, если вы видите только часть записи вместо ее полного текста.

Создайте вторую запись для темы Chess и одну запись для темы Rock Climbing, чтобы у нас были исходные данные для дальнейшей разработки Learning Log.

 

Интерактивная оболочка Django

Введенные данные можно проанализировать на программном уровне в интерактивном терминальном сеансе. Эта интерактивная среда, называемая оболочкой (shell) Django, прекрасно подходит для тестирования и диагностики проекта. Пример сеанса в интерактивной оболочке:

(ll_env)learning_log$ python manage.py shell

(1) >>> from learning_logs.models import Topic

>>> Topic.objects.all()

[, ]

Команда python manage.py shell (выполняемая в активной виртуальной среде) запускает интерпретатор Python, который может использоваться для работы с информацией в базе данных проекта. В данном случае мы импортируем модель Topic из модуля learning_logs.models (1) . Затем метод Topic.objects.all() используется для получения всех экземпляров модели Topic; возвращаемый список называется итоговым набором (queryset).

Содержимое итогового набора перебирается точно так же, как и содержимое списка. Например, просмотр идентификаторов, назначенных каждому объекту темы, выполняется так:

>>> topics = Topic.objects.all()

>>> for topic in topics:

... print(topic.id, topic)

...

1 Chess

2 Rock Climbing

Итоговый набор сохраняется в topics, после чего выводится атрибут id каждого объекта topic и его строковое представление. Мы видим, что теме Chess присвоен идентификатор 1, а Rock Climbing присвоен идентификатор 2.

Зная идентификатор конкретного объекта, можно получить этот объект и проанализировать содержащиеся в нем атрибуты. Просмотрим значения text и date_added для темы Chess:

>>> t = Topic.objects.get(id=1)

>>> t.text

'Chess'

>>> t.date_added

datetime.datetime(2015, 5, 28, 4, 39, 11, 989446, tzinfo=)

Также можно просмотреть записи, относящиеся к конкретной теме. Ранее мы определили атрибут topic для модели Entry. Он был экземпляром ForeighKey, представляющим связь между записью и темой. Django может использовать эту связь для получения всех записей, относящихся к некоторой теме:

(1) >>> t.entry_set.all()

[,

the opening phase of the game, it's important t...>]

Чтобы получить данные через отношение внешнего ключа, используйте имя связанной модели, записанное в нижнем регистре, за которым следует символ подчеркивания и слово set (1) . Допустим, у вас имеются модели Pizza и Topping, и модель Topping связана с Pizza через внешний ключ. Если ваш объект называется my_pizza, для получения всех связанных с ним экземпляров Topping используется выражение my_pizza.topping_set.all().

Мы будем использовать такой синтаксис при переходе к программированию страниц, которые могут запрашиваться пользователями. Оболочка очень удобна тогда, когда вы хотите проверить, что ваш код получает нужные данные. Если в оболочке код работает так, как задумано, можно ожидать, что он будет правильно работать и в файлах, которые вы создаете в своем проекте. Если код выдает ошибки или не загружает те данные, которые должен загружать, вам будет намного проще отладить его в простой оболочке, чем при работе с файлами, генерирующими веб-страницы. В книге мы не будем часто возвращаться к оболочке, но вам не стоит забывать о ней — это полезный инструмент, который поможет вам освоить синтаксис Django для работы с данными проекта.

Примечание

При каждом изменении модели необходимо перезапустить оболочку, чтобы увидеть результаты этих изменений. Чтобы завершить сеанс работы с оболочкой, нажмите Сtrl+D; в Windows нажмите Ctrl+Z, а затем Enter.

Упражнения

18-2. Короткие записи: метод __str__() в модели Entry в настоящее время присоединяет многоточие к каждому экземпляру Entry, отображаемому Django на административном сайте или в оболочке. Добавьте в метод __str__() команду if, добавляющую многоточие только для записей, длина которых превышает 50 символов. Воспользуйтесь административным сайтом, чтобы ввести запись с длиной менее 50 символов, и убедитесь в том, что при ее просмотре многоточие не отображается.

18-3. Django API: при написании кода для работы с данными проекта вы создаете запрос. Просмотрите документацию по созданию запросов к данным по адресу https://docs.djangoproject.com/en/1.8/topics/db/queries/. Многое из того, что вы увидите, покажется вам новым, но эта информация пригодится вам, когда вы начнете работать над собственными проектами.

18-4. Пиццерия: создайте новый проект с именем pizzeria, содержащий приложение pizzas. Определите модель Pizza с полем name, в котором хранятся названия видов пиццы (например, «Гавайская» или «Четыре сыра»). Определите модель Topping с полями pizza и name. Поле pizza должно содержать внешний ключ к модели Pizza, а поле name должно позволять хранить такие значения, как «ананас» или «грибы».

Зарегистрируйте обе модели на административном сайте. Используйте сайт для ввода названий пиццы и дополнений. Изучите введенные данные в интерактивной оболочке.

 

Создание страниц: домашняя страница Learning Log

Обычно процесс создания веб-страниц в Django состоит из трех стадий: определения URL, написания представлений и написания шаблонов. Сначала следует определить схемы (patterns) URL. Схема URL описывает структуру URL-адреса и сообщает Django, на какие компоненты следует обращать внимание при сопоставлении запроса браузера с URL-адресом на сайте, чтобы выбрать возвращаемую страницу.

Затем каждый URL-адрес связывается с конкретным представлением — функция представления читает и обрабатывает данные, необходимые странице. Функция представления часто вызывает шаблон, который строит страницу, подходящую для передачи браузеру. Чтобы вы лучше поняли, как работает этот механизм, создадим домашнюю страницу для приложения Learning Log. Мы определим URL-адрес домашней страницы, напишем для него функцию представления и создадим простой шаблон.

Так как мы сейчас всего лишь убеждаемся в том, что Learning Log работает, как положено, страница пока останется простой. Когда приложение будет завершено, вы можете заниматься его оформлением, сколько душе угодно; приложение, которое хорошо выглядит, но не работает, бессмысленно. Пока на домашней странице будет отображаться только заголовок и краткое описание.

 

Сопоставление URL

Пользователь запрашивает страницы, вводя URL-адреса в браузере и щелкая на ссылках, поэтому мы должны решить, какие URL-адреса понадобятся в нашем проекте. Начнем с URL домашней страницы: это базовый адрес, используемый для обращения к проекту. На данный момент базовый URL-адрес http://localhost:8000/ возвращает сайт, сгенерированный Django по умолчанию; он сообщает о том, что проект был создан успешно. Мы изменим домашнюю страницу, связав базовый URL-адрес с домашней страницей Learning Log.

В каталоге проекта learning_log откройте файл urls.py. Вы увидите в нем следующий код:

urls.py

(1) from django.conf.urls import include, url

from django.contrib import admin

(2)urlpatterns = [

(3) . .url(r'^admin/', include(admin.site.urls)),

]

Первые две строки импортируют функции и модули, управляющие URL-адресами проекта и административным сайтом (1) . В теле файла определяется переменная urlpatterns (2). В файле urls.py, представляющем проект в целом, переменная urlpatterns включает наборы URL-адресов из приложений в проект. Код (3) включает модуль admin.site.urls, определяющий все URL-адреса, которые могут запрашиваться с административного сайта.

Добавим в этот файл URL-адреса learning_logs:

from django.conf.urls import include, url

from django.contrib import admin

urlpatterns = [

url(r'^admin/', include(admin.site.urls)),

(1) . .url(r'', include('learning_logs.urls', namespace='learning_ logs')),

]

В точке (1) добавляется строка включения модуля learning_logs.urls. Эта строка включает аргумент namespace, по которому URL-адреса learning_logs можно отличить от других URL-адресов, которые могут появиться в проекте. Данная возможность может оказаться чрезвычайно полезной по мере роста проекта.

Файл urls.py по умолчанию находится в каталоге learning_log; теперь нужно создать второй файл urls.py в папке learning_logs:

urls.py

(1) """Определяет схемы URL для learning_logs."""

(2)from django.conf.urls import url

(3)from . import views

(4)urlpatterns = [

. .# Домашняя страница

(5) . .url(r'^$', views.index, name='index'),

]

Чтобы было понятно, с какой версией urls.py мы работаем, в начало файла добавляется строка документации (1) . Затем импортируется функция url, она необходима для связывания URL с представлениями (2). Также импортируется модуль views (3); точка приказывает Python импортировать представления из каталога, в котором находится текущий модуль urls.py. Переменная urlpatterns в этом модуле представляет собой список страниц, которые могут запрашиваться из приложения learning_logs (4).

Схема URL представляет собой вызов функции url() с тремя аргументами (5). Первый аргумент содержит регулярное выражение. Django ищет в urlpatterns регулярное выражение, совпадающее со строкой запрашиваемого URL. Таким образом, регулярное выражение должно определять схему адреса, которую будет искать Django.

Возьмем регулярное выражение r'^$'. Символ r сообщает Python, что последующая строка должна интерпретироваться без дополнительной обработки, а апострофы указывают, где начинается и заканчивается регулярное выражение. Символ ^ обозначает начало строки, а символ $ обозначает ее конец. В целом это выражение приказывает Python искать URL-адрес, не содержащий ничего между началом и концом URL. Python игнорирует базовый URL-адрес проекта (http://localhost:8000/), так что пустое регулярное выражение совпадает с базовым URL-адресом. Любой другой URL-адрес не будет соответствовать этому выражению, и Django вернет страницу с ошибкой, если запрашиваемый URL не соответствует ни одной из существующих схем URL.

Второй аргумент url() (5) определяет вызываемую функцию представления. Когда запрашиваемый URL-адрес соответствует регулярному выражению, Django вызывает views.index (мы напишем эту функцию представления в следующем разделе). Третий аргумент определяет index для этой схемы URL, чтобы на нее можно было ссылаться в других частях кода. Каждый раз, когда потребуется предоставить ссылку на домашнюю страницу, мы будем использовать это имя вместо URL.

Примечание

Регулярные выражения используются почти во всех языках программирования. Они чрезвычайно полезны, но, чтобы освоить их, понадобится время. Если что-то осталось непонятным, не беспокойтесь; в ходе работы над проектом мы рассмотрим много примеров.

 

Написание представления

Функция представления получает информацию из запроса, подготавливает данные, необходимые для построения страницы, и возвращает данные браузеру — часто с использованием шаблона, определяющего внешний вид страницы.

Файл views.py в learning_logs был сгенерирован автоматически при выполнении команды python manage.py startapp. На данный момент его содержимое выглядит так:

views.py

from django.shortcuts import render

# Создайте здесь свои представления.

Сейчас файл только импортирует функцию render(), которая генерирует ответ на основании данных, полученных от представлений. Представление домашней страницы должно быть записано в следующем виде:

from django.shortcuts import render

def index(request):

. ."""Домашняя страница приложения Learning Log"""

. .return render(request, 'learning_logs/index.html')

Если URL запроса совпадает с только что определенной схемой, Django ищет в файле views.py функцию с именем index(), после чего передает этой функции представления объект запроса. В нашем случае никакая обработка данных для страницы не нужна, поэтому код функции сводится к вызову render(). Функция render() использует два аргумента — исходный объект запроса и шаблон, используемый для построения страницы. Давайте напишем этот шаблон.

 

Написание шаблона

Шаблон задает структуру веб-страницы. Он определяет общий внешний вид, а Django заполняет его соответствующими данными при каждом запросе страницы. Шаблон может обращаться к любым данным, полученным от представления. Так как наше представление домашней страницы никаких данных не предоставляет, шаблон получается относительно простым.

В каталоге learning_logs создайте новый каталог с именем templates. В каталоге templates создайте другой каталог с именем learning_logs. На первый взгляд такая структура кажется избыточной (каталог learning_logs в каталоге templates внутри каталога learning_logs), но созданная таким образом структура будет однозначно интерпретироваться Django даже в контексте большого проекта, состоящего из множества отдельных приложений. Во внутреннем каталоге learning_logs создайте новый файл с именем index.html. Включите в него следующий текст:

index.html

Learning Log

Learning Log helps you keep track of your learning, for any topic you're

learning about.

Это очень простой файл. Если вы не знакомы с синтаксисом HTML, теги

обозначают абзацы. Тег

открывает абзац, а тег

закрывает его. Наша страница содержит два абзаца: первый содержит заголовок, а второй описывает, что пользователь может сделать с помощью приложения Learning Log.

Теперь при запросе базового URL-адреса проекта http://localhost:8000/ вы увидите только что построенную страницу вместо страницы по умолчанию. Django берет запрошенный URL-адрес и видит, что он совпадает со схемой r'^$'; в этом случае Django вызывает функцию views.index(), что приводит к построению страницы с использованием шаблона, содержащегося в index.html. Полученная страница показана на рис. 18.3.

Рис. 18.3. Домашняя страница Learning Log

И хотя может показаться, что для одной страницы этот процесс слишком сложен, такое разделение URL-адресов, представлений и шаблонов работает хорошо. Оно позволяет сосредоточиться на отдельных аспектах проекта, а в более крупных проектах отдельные участники могут сосредоточиться на тех областях, в которых они наиболее сильны. Например, специалист по базам данных может заняться моделями, программист — кодом представления, а веб-дизайнер — шаблонами.

Упражнения

18-5. План питания: представьте приложение для составления плана питания на неделю. Создайте новый каталог с именем meal_planner, затем создайте в этом каталоге новый проект Django. Создайте новое приложение с именем meal_plans. Постройте простую домашнюю страницу для этого проекта.

18-6. Домашняя страница Pizzeria: добавьте домашнюю страницу в проект Pizzeria, который вы начали строить в упражнении 18-4 (с. 394).

 

Построение других страниц

Теперь, когда вы начали представлять процесс построения страниц, можно переходить к построению проекта Learning Log. Мы создадим две страницы для вывода данных: на одной будет выводиться список всех тем, а на другой — все записи по конкретной теме. Для каждой страницы мы создадим схему URL, напишем функцию представления и создадим шаблон. Но, прежде чем переходить к работе, стоит создать базовый шаблон, от которого будут наследовать все шаблоны этого проекта.

 

Наследование шаблонов

При построении сайта некоторые элементы почти всегда повторяются на каждой странице. Вместо того чтобы встраивать эти элементы непосредственно в страницы, вы можете написать базовый шаблон с повторяющимися элементами; все страницы будут наследовать от этого шаблона. Такое решение позволит сосредоточиться на разработке уникальных аспектов каждой страницы и существенно упростит изменение общего оформления проекта в целом.

 

Родительский шаблон

Начнем с создания шаблона base.html в одном каталоге с файлом index.html. Этот файл будет содержать элементы, общие для всех страниц; все остальные шаблоны наследуют от base.html. Пока единственным элементом, который должен повторяться на каждой странице, остается заголовок в верхней части страницы. Так как шаблон будет включаться в каждую страницу, преобразуем заголовок в ссылку на домашнюю страницу:

base.html

(1) Learning Log

(2){% block content %}{% endblock content %}

Первая часть файла создает абзац с именем проекта, который также работает как ссылка на домашнюю страницу. Для построения ссылки использовался шаблонный тег, обозначенный фигурными скобками и знаками % {% %}. Шаблонный тег представляет собой блок кода, который генерирует информацию для вывода на странице. В данном примере шаблонный тег {% url 'learning_logs:index' %} генерирует URL-адрес, соответствующий схеме URL, определенной в файле learning_logs/urls.py с именем 'index' (1) . В данном примере learning_logs — пространство имен, а index — схема URL с уникальным именем в этом пространстве имен.

В этой простой странице HTML ссылка заключается в якорный тег:

текст ссылки

Генерирование URL-адреса шаблонным тегом существенно упрощает актуализацию ссылок. Чтобы изменить URL-адрес в проекте, достаточно изменить схему URL в urls.py, а Django автоматически вставит обновленный URL-адрес при ­следующем запросе страницы. Каждая страница в проекте будет наследовать от base.html, так что в дальнейшем на каждой странице будет содержаться ссылка на домашнюю страницу.

В точке (2) вставляется пара тегов block. Блок с именем content резервирует место; информация, попадающая в блок content, будет определяться дочерним шаблоном.

Дочерний шаблон не обязан определять каждый блок в своем родителе, так что в родительских шаблонах можно зарезервировать место для любого количества блоков, а дочерний шаблон будет использовать столько из них, сколько по­требуется.

Примечание

В коде Python почти всегда используются отступы в четыре пробела. Файлы шаблонов обычно имеют больший уровень вложенности, чем файлы Python, поэтому каждый уровень отступа обычно обозначается двумя пробелами.

 

Дочерний шаблон

Теперь нужно переписать файл index.html так, чтобы он наследовал от base.html. Обновленный файл index.html выглядит так:

index.htm

(1) {% extends "learning_logs/base.html" %}

(2){% block content %}

Learning Log helps you keep track of your learning, for any topic you're

learning about.

(3){% endblock content %}

Сравнивая этот файл с исходной версией index.html, мы видим, что заголовок Learning Log заменен кодом наследования от родительского шаблона (1) . В первой строке дочернего шаблона должен находиться тег {% extends %}, который сообщает Django, от какого родительского шаблона он наследует. Файл base.html является частью learning_logs, поэтому learning_logs включается в путь к родительскому шаблону. Эта строка извлекает все содержимое из шаблона base.html и позволяет index.html определить, что должно попасть в пространство, зарезервированное блоком content.

Блок content определяется в точке (2) вставкой тега {% block %} с именем content. Все, что не наследуется от родительского шаблона, попадает в блок content. В ­данном случае это абзац с описанием проекта Learning Log. В точке (3) мы ­сообщаем о том, что определение content завершено, при помощи тега {% endblock content %}.

Вероятно, вы уже начинаете понимать преимущества наследования шаблонов: в дочерний шаблон достаточно включить информацию, уникальную для этой страницы. Такой подход упрощает не только каждый шаблон, но и изменение сайта. Чтобы изменить элемент, общий для многих страниц, достаточно изменить элемент в родительском шаблоне. Внесенные изменения будут автоматически перенесены на каждую страницу, наследующую от этого шаблона. В проекте из десятков и сотен страниц такая структура значительно упрощает и ускоряет доработку сайта.

Примечание

В больших проектах часто создается один родительский шаблон base.html для всего сайта и родительские шаблоны для каждого крупного раздела сайта. Все шаблоны разделов наследуют от base.html, и каждая страница сайта наследует от шаблона раздела. При такой структуре вы сможете легко изменять оформление и поведение сайта в целом, любого его раздела или ­отдельной страницы. Данная конфигурация сильно повышает эффективность работы и стимулирует разработчика к дальнейшему совершенствованию сайта.

 

Страница со списком тем

Разобравшись с тем, как эффективно организовать построение страниц, мы можем сосредоточиться на следующих двух страницах: списке всех тем и списке записей по одной теме. На странице тем выводится перечень всех тем, созданных пользователями, и это первая страница, на которой нам придется работать с данными.

 

Схема URL для тем

Сначала нужно определить URL для страницы тем. Обычно в таких случаях выбирается простой фрагмент URL, который отражает суть информации, представленной на странице. Мы воспользуемся словом topics, так что для получения страницы будет использоваться URL http://localhost:8000/topics/. А вот какие изменения следует внести в learning_logs/urls.py:

urls.py

"""Определяет схемы URL для learning_logs."""

...

urlpatterns = [

# Домашняя страница

url(r'^$', views.index, name='index'),

. .# Вывод всех тем.

(1) . .url(r'^topics/$', views.topics, name='topics'),

]

Мы просто добавили topics/ в аргумент регулярного выражения, используемый с URL-адресом домашней страницы (1) . Когда Django проверяет запрашиваемый URL-адрес, эта схема совпадет с любым URL-адресом, который состоит из базового URL-адреса и слова topics. Косую черту в конце можно включить, можно не включать, но после слова topics ничего быть не должно, иначе схема не совпадет. Любой запрос с URL-адресом, соответствующим этой схеме, будет передан функции topics() в views.py.

 

Представление topics

Функция topics() должна получать данные из базы данных и отправлять их шаблону. Обновленная версия views.py выглядит так:

views.py

from django.shortcuts import render

(1) from .models import Topic

def index(request):

...

(2)def topics(request):

. ."""Выводит список тем."""

(3) . .topics = Topic.objects.order_by('date_added')

(4) . .context = {'topics': topics}

(5) . .return render(request, 'learning_logs/topics.html', context)

Сначала импортируется модель, связанная с нужными данными (1) . Функции topics() необходим один параметр: объект запроса, полученный Django от сер­вера (2). В точке (3) выдается запрос к базе данных на получение объектов Topic, отсортированных по атрибуту date_added. Полученный итоговый набор сохраняется в topics.

В точке (4) определяется контекст, который будет передаваться шаблону. Контекст представляет собой словарь, в котором ключами являются имена, используемые в шаблоне для обращения к данным, а значениями — данные, которые должны передаваться шаблону. В данном случае существует всего одна пара «ключ—значение», которая содержит набор тем, отображаемых на странице. При построении страницы, использующей данные, функции render() передается переменная context, а также объект request и путь к шаблону (5).

 

Шаблон topics

Шаблон страницы со списком тем получает словарь context, чтобы шаблон мог использовать данные, предоставленные topics(). Создайте файл с именем topics.html в одном каталоге с index.html. Вывод списка тем в шаблоне осуществляется следующим образом:

topics.html

{% extends "learning_logs/base.html" %}

{% block content %}

Topics

(1)

    (2) . .{% for topic in topics %}

    (3) . .

  • {{ topic }}
  • (4) . .{% empty %}

    . .

  • No topics have been added yet.
  • (5) . .{% endfor %}

    ?

{% endblock content %}

Сначала тег {% extends %} объявляет о наследовании от base.html, как и в случае с шаблоном index, после чего открывается блок content. Тело страницы содержит маркированный (bulleted) список введенных тем. В стандартном языке HTML маркированный список называется неупорядоченным списком и обозначается тегами

    . Список тем начинается в точке (1) .

    В точке (2) находится другой шаблонный тег, эквивалентный циклу for для перебора списка тем из словаря context. Код, используемый в шаблоне, отличается от Python в нескольких важных отношениях. Python использует отступы для ­обозначения строк, входящих в тело цикла. В шаблоне каждый цикл for должен снабжаться явным тегом {% endfor %}, обозначающим конец цикла. Таким образом, в шаблонах часто встречаются циклы следующего вида:

    {% for элемент in список %}

    . .действия для каждого элемента

    {% endfor %}

    В цикле каждая тема должна быть преобразована в элемент маркированного списка. Чтобы вывести значение переменной в шаблоне, заключите ее имя в двойные фигурные скобки. Код {{ topic }} в точке (3) будет заменен значением topic при каждом проходе цикла. Фигурные скобки на странице не появятся; они всего лишь сообщают Django об использовании шаблонной переменной. Тег HTML

  • обозначает элемент списка. Все, что находится между тегами, в паре тегов
      , будет отображаться как элемент маркированного списка.

      В точке (4) находится шаблонный тег {% empty %}, который сообщает Django, что делать при отсутствии элементов в списке. В нашем примере выводится сообщение о том, что темы еще не созданы. Последние две строки завершают цикл for (5) и маркированный список ?.

      Затем необходимо изменить базовый шаблон и включить ссылку на страницу с темами:

      base.html

      (1) Learning Log -

      (2) Topics

      {% block content %}{% endblock content %}

      После ссылки на домашнюю страницу (1) добавляется дефис, после которого вставляется ссылка на страницу тем, которая также представлена шаблонным тегом (2). Эта строка приказывает Django сгенерировать ссылку, соответствующую схеме URL с именем 'topics', в learning_logs/urls.py.

      Обновив домашнюю страницу в браузере, вы увидите ссылку Topics. Щелчок на этой ссылке открывает страницу, похожую на рис. 18.4.

      Рис. 18.4. Страница со списком тем

       

      Страницы отдельных тем

      Следующим шагом станет создание страницы для вывода информации по одной теме, с названием темы и всеми записями по этой теме. Мы снова определим новую схему URL, напишем представление и создадим шаблон. Кроме того, на странице со списком тем каждый элемент маркированного списка будет преобразован в ­ссылку на соответствующую страницу отдельной темы.

       

      Схема URL для отдельных тем

      Схема URL для страницы отдельной темы немного отличается от других схем URL, которые встречались нам ранее, потому что в ней используется атрибут id темы для обозначения запрашиваемой темы. Например, если пользователь хочет просмотреть страницу с подробной информацией по теме Chess (id=1), эта страница будет иметь URL-адрес http://localhost:8000/topics/1/. Вот как выглядит схема для этого URL-адреса из learning_logs/urls.py:

      urls.py

      ...

      urlpatterns = [

      ...

      . .# Страница с подробной информацией по отдельной теме

      . .url(r'^topics/(?P\d+)/$', views.topic, name='topic'),

      ]

      Рассмотрим регулярное выражение в схеме URL, r'^topics/(?P\d+)/$'. Символ r сообщает Django, что последующая строка должна интерпретироваться без дополнительной обработки, а выражение заключено в кавычки. Вторая часть выражения, /(?P\d+)/, описывает целое число, заключенное между двумя косыми чертами; это целое число сохраняется в аргументе topic_id. Круглые скобки, в которые заключена эта часть выражения, сохраняют значение из URL; часть ?P сохраняет совпавшее значение в topic_id; а выражение \d+ ­совпадает с любым количеством цифр, заключенных между символами косой черты.

      Когда Django находит URL-адрес, соответствующий этой схеме, вызывается функция представления topic(), в аргументе которой передается значение, хранящееся в topic_id. Значение topic_id используется для получения нужной темы внутри функции.

       

      Представление отдельной темы

      Функция topic() должна получить тему и все связанные с ней записи из базы данных:

      views.py

      --snip--

      (1) def topic(request, topic_id):

      . ."""Выводит одну тему и все ее записи."""

      (2) . .topic = Topic.objects.get(id=topic_id)

      (3) . .entries = topic.entry_set.order_by('-date_added')

      (4) . .context = {'topic': topic, 'entries': entries}

      (5) . .return render(request, 'learning_logs/topic.html', context)

      Это первая функция представления, которой требуется параметр, отличный от объекта запроса. Функция получает значение, совпавшее с выражением (?P\d+), и сохраняет его в topic_id (1) . В точке (2) функция get() ­используется для получения темы (по аналогии с тем, как мы это делали в оболочке Django). В точке (3) загружаются записи, связанные с данной темой, и они упорядочиваются по значению date_added: знак «минус» перед date_added сортирует результаты в обратном порядке, то есть самые последние записи будут находиться на первых местах. Тема и записи сохраняются в словаре context (4), который передается ­шаблону topic.html (5).

      Примечание

      Выражения в строках (2) и (3), обращающиеся к базе данных за конкретной информацией, называются «запросами». Когда вы пишете подобные запросы для своих проектов, сначала опробуйте их в оболочке Django. Вы сможете проверить результат намного быстрее, чем если напишете представление и шаблон, а затем проверите результаты в браузере.

       

      Шаблон отдельной темы

      В шаблоне должно отображаться название темы и текст записей. Также необходимо сообщить пользователю, если по теме еще не было сделано ни одной записи:

      topic.html

      {% extends 'learning_logs/base.html' %}

      {% block content %}

      (1)

      Topic: {{ topic }}

      Entries:

      (2)

        (3) {% for entry in entries %}

        . .

      • (4) . .

        {{ entry.date_added|date:'M d, Y H:i' }}

        (5) . .

        {{ entry.text|linebreaks }}

        . .

      • ? {% empty %}

        . .

      • . . There are no entries for this topic yet.

        . .

      • {% endfor %}

      {% endblock content %}

      Шаблон расширяет base.html, как и для всех страниц проекта. Затем выводится текущая тема (1) из шаблонной переменной {{ topic }}. Переменная topic доступна, потому что она включена в словарь context. Затем создается маркированный список со всеми записями по теме (2); перебор записей осуществляется так же, как это делалось ранее для тем (3).

      С каждым элементом списка связываются два значения: временна?я метка и полный текст каждой записи. Для временнуй метки (4) выводится значение атрибута date_added. В шаблонах Django вертикальная черта (|) представляет фильтр — функцию, изменяющую значение шаблонной переменной. Фильтр date:'M d, Y H:i' выводит временны?е метки в формате January 1, 2015 23:00. Следующая строка выводит полное значение text (вместо первых 50 символов каждой записи). Фильтр linebreaks (5) следит за тем, чтобы длинный текст содержал разрывы строк в формате, поддерживаемом браузером (вместо блока непрерывного текста). В точке ? шаблонный тег {% empty %} используется для вывода сообщения об отсутствии записей.

       

      Ссылки на странице

      Прежде чем просматривать страницу отдельной темы в браузере, необходимо изменить шаблон списка тем, чтобы каждая тема вела на соответствующую страницу. Внесите следующие изменения в topics.html:

      topics.html

      ...

      {% for topic in topics %}

      . .

    • . . . .{{ topic }}

      . .

    • {% empty %}

      ...

      Шаблонный тег URL используется для генерирования ссылки на основании схемы URL из learning_logs с именем 'topic'. Этой схеме URL необходим аргумент topic_id, поэтому в шаблонный тег URL добавляется атрибут topic.id. Теперь каждая тема в списке представляет собой ссылку на страницу темы, например http://localhost:8000/topics/1/.

      Если теперь обновить страницу тем и щелкнуть на теме, открывается страница, изображенная на рис. 18.5.

      Рис. 18.5. Страница со списком всех записей по отдельной теме

      Упражнения

      18-7. Документация шаблона: просмотрите документацию по шаблонам Django по адресу https://docs.djangoproject.com/en/1.8/ref/templates/. Используйте ее в работе над собственными проектами.

      18-8. Страницы Pizzeria: добавьте страницу в проект Pizzeria из упражнения 18-6 (с. 398) с названиями видов пиццы. Свяжите каждое название пиццы со страницей, на которой выводится список дополнений к этой пицце. Обязательно примените наследование шаблонов, чтобы повысить эффективность построения страниц.

       

      Итоги

      В этой главе вы начали осваивать построение веб-приложений с использованием инфраструктуры Django. Вы написали короткую спецификацию проекта, установили Django в виртуальной среде, узнали, как настроить проект, и проверили правильность настройки. Вы узнали, как создать приложение и как определить модели для представления данных в вашем приложении. Также были рассмотрены базы данных, и вы узнали, как Django упрощает миграцию баз данных после внесения изменений в модель. Вы научились создавать суперпользователей для административного сайта, а также использовали административный сайт для ввода исходных данных.

      Также в этой главе была представлена оболочка Django, позволяющая работать с данными проекта в терминальном сеансе. Вы научились определять URL-адреса, создавать функции представления и писать шаблоны для построения страниц сайта. Наконец, вы применили механизм наследования шаблонов, который упрощает структуру отдельных шаблонов и модификацию сайта по мере развития проекта.

      В главе 19 мы создадим интуитивно понятные, удобные страницы, на которых пользователи смогут добавлять новые темы и записи, а также редактировать существующие записи без участия административного сайта. Кроме того, будет добавлена система регистрации пользователей, чтобы любой пользователь мог создать учетную запись и вести свой журнал. Собственно, в этом и заключается сущность веб-приложения — создание функциональности, с которой может взаимодействовать любое количество пользователей.