Книга Изучаем Python
Скачать 4.68 Mb.
|
403 для обозначения строк, входящих в тело цикла. В шаблоне каждый цикл for должен снабжаться явным тегом {% endfor %} , обозначающим конец цикла. Таким образом, в шаблонах часто встречаются циклы следующего вида: {% for элемент in список %} действия для каждого элемента {% endfor %} В цикле каждая тема должна быть преобразована в элемент маркированного спи- ска. Чтобы вывести значение переменной в шаблоне, заключите ее имя в двойные фигурные скобки. Код {{ topic }} в точке будет заменен значением topic при каждом проходе цикла. Фигурные скобки на странице не появятся; они всего лишь сообщают Django об использовании шаблонной переменной. Тег HTML обозначает элемент списка. Все, что находится между тегами, в паре тегов , будет отображаться как элемент маркированного списка. В точке находится шаблонный тег {% empty %} , который сообщает Django, что делать при отсутствии элементов в списке. В нашем примере выводится сообще- ние о том, что темы еще не созданы. Последние две строки завершают цикл for и маркированный список Затем необходимо изменить базовый шаблон и включить ссылку на страницу с темами: base.html Learning Log - Topics {% block content %}{% endblock content %} После ссылки на домашнюю страницу добавляется дефис, после которого встав- ляется ссылка на страницу тем, которая также представлена шаблонным тегом . Эта строка приказывает Django сгенерировать ссылку, соответствующую схеме URL с именем 'topics' , в learning_logs/urls .py Обновив домашнюю страницу в браузере, вы увидите ссылку Topics . Щелчок на этой ссылке открывает страницу, похожую на рис. 18.4. Рис. 18.4. Страница со списком тем 404 Глава 18 • Знакомство с Django Страницы отдельных тем Следующим шагом станет создание страницы для вывода информации по одной теме, с названием темы и всеми записями по этой теме. Мы снова определим новую схему 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 ] Рассмотрим регулярное выражение в схеме URL, r'^topics/(?P Символ r сообщает Django, что последующая строка должна интерпретироваться без дополнительной обработки, а выражение заключено в кавычки. Вторая часть выражения, /(?P , описывает целое число, заключенное между двумя косыми чертами; это целое число сохраняется в аргументе topic_id . Круглые скоб- ки, в которые заключена эта часть выражения, сохраняют значение из URL; часть ?P сохраняет совпавшее значение в topic_id ; а выражение \d+ совпадает с любым количеством цифр, заключенных между символами косой черты. Когда Django находит URL-адрес, соответствующий этой схеме, вызывается функ- ция представления topic() , в аргументе которой передается значение, хранящееся в topic_id . Значение topic_id используется для получения нужной темы внутри функции. Представление отдельной темы Функция topic() должна получить тему и все связанные с ней записи из базы данных: views.py --snip-- def topic(request, topic_id): """Выводит одну тему и все ее записи.""" topic = Topic.objects.get(id=topic_id) entries = topic.entry_set.order_by('-date_added') context = {'topic': topic, 'entries': entries} return render(request, 'learning_logs/topic.html', context) Построение других страниц 405 Это первая функция представления, которой требуется параметр, отличный от объекта запроса. Функция получает значение, совпавшее с выражени- ем (?P , и сохраняет его в topic_id . В точке функция get() используется для получения темы (по аналогии с тем, как мы это делали в оболочке Django). В точке загружаются записи, связанные с данной темой, и они упоря- дочиваются по значению date_added : знак «минус» перед date_added сортирует результаты в обратном порядке, то есть самые последние записи будут находиться на первых местах. Тема и записи сохраняются в словаре context , который пере- дается шаблону topic .html . ПРИМЕЧАНИЕ Выражения в строках и , обращающиеся к базе данных за конкретной информацией, называ- ются «запросами» . Когда вы пишете подобные запросы для своих проектов, сначала опробуйте их в оболочке Django . Вы сможете проверить результат намного быстрее, чем если напишете пред- ставление и шаблон, а затем проверите результаты в браузере . Шаблон отдельной темы В шаблоне должно отображаться название темы и текст записей. Также необходимо сообщить пользователю, если по теме еще не было сделано ни одной записи: topic.html {% extends 'learning_logs/base.html' %} {% block content %} Topic: {{ topic }} Entries:
{% for entry in entries %} {% empty %} {% endfor %} {% endblock content %} Шаблон расширяет base .html , как и для всех страниц проекта. Затем выводится текущая тема из шаблонной переменной {{ topic }} . Переменная topic доступ- на, потому что она включена в словарь context . Затем создается маркированный список со всеми записями по теме ; перебор записей осуществляется так же, как это делалось ранее для тем . 406 Глава 18 • Знакомство с Django С каждым элементом списка связываются два значения: временна́я метка и пол- ный текст каждой записи. Для временнуй метки выводится значение атрибута date_added . В шаблонах Django вертикальная черта ( | ) представляет фильтр — функцию, изменяющую значение шаблонной переменной. Фильтр date:'M d, Y H:i' выводит временны́е метки в формате January 1, 2015 23:00. Следующая строка выводит полное значение text (вместо первых 50 символов каждой запи- си). Фильтр linebreaks следит за тем, чтобы длинный текст содержал разрывы строк в формате, поддерживаемом браузером (вместо блока непрерывного текста). В точке шаблонный тег {% 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. Страница со списком всех записей по отдельной теме Итоги 407 УПРАЖНЕНИЯ 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 мы создадим интуитивно понятные, удобные страницы, на которых пользователи смогут добавлять новые темы и записи, а также редактировать суще- ствующие записи без участия административного сайта. Кроме того, будет добав- лена система регистрации пользователей, чтобы любой пользователь мог создать учетную запись и вести свой журнал. Собственно, в этом и заключается сущность веб-приложения — создание функциональности, с которой может взаимодейство- вать любое количество пользователей. 19 Учетные записи пользователей Что является самым главным для веб-приложения? Что любой пользователь, жи- вущий в любой стране мира, сможет создать учетную запись в вашем приложении и начать работать с ним. В этой главе мы построим формы, на которых пользова- тели смогут вводить свои темы и записи, а также редактировать существующие данные. Кроме того, вы узнаете, как Django защищает приложения от распростра- ненных атак на страницы с формами, чтобы вам не приходилось тратить много времени на продумывание средств защиты вашего приложения. Затем будет реализована система проверки пользователей. Мы создадим стра- ницу регистрации, на которой пользователи смогут создавать учетные записи, и ограничим доступ к некоторым страницам для анонимных пользователей. Затем некоторые функции представления будут изменены так, чтобы пользователь мог видеть только свои собственные данные. Вы узнаете, как обеспечить безопасность и конфиденциальность данных пользователей. Редактирование данных Прежде чем строить систему аутентификации пользователей для создания учетных записей, сначала мы добавим несколько страниц, на которых пользователи смогут вводить собственные данные. У пользователей появится возможность создавать новые темы, добавлять новые записи и редактировать записи, сделанные ранее. В настоящее время данные могут вводиться только суперпользователем на админи- стративном сайте. Однако разрешать пользователям работу на административном сайте явно нежелательно, поэтому мы воспользуемся средствами построения форм Django для создания страниц, на которых пользователи смогут вводить данные. Добавление новых тем Начнем с возможности создания новых тем. Страницы на базе форм добавляются практически так же, как и те страницы, которые мы уже строили ранее: вы опреде- ляете URL, пишете функцию представления и создаете шаблон. Принципиальное отличие — добавление нового модуля forms .py , содержащего функциональность форм. Редактирование данных 409 Объект ModelForm Любая страница, на которой пользователь может вводить и отправлять инфор- мацию, является формой, даже если на первый взгляд она на форму не похожа. Когда пользователь вводит информацию, необходимо проверить, что он ввел корректные данные, а не вредоносный код (например, код для нарушения ра- боты сервера). Затем проверенная информация обрабатывается и сохраняется в нужном месте базы данных. Django автоматизирует бульшую часть этой работы. Простейший способ построения форм в Django основан на использовании класса ModelForm , который автоматически строит форму на основании моделей, опреде- ленных в главе 18. Ваша первая форма будет создана в файле forms .py , который должен находиться в одном каталоге с models .py : forms.py from django import forms from .models import Topic class TopicForm(forms.ModelForm): class Meta: model = Topic fields = ['text'] labels = {'text': ''} Сначала импортируется модуль forms и модель, с которой мы будем работать: Topic . В точке определяется класс с именем TopicForm , наследующий от forms. ModelForm . Простейшая версия ModelForm состоит из вложенного класса Meta , который сообщает Django, на какой модели должна базироваться форма и какие поля на ней должны находиться. В точке форма создается на базе модели Topic , а на ней размещается только поле text . Код приказывает Django не генериро- вать подпись для текстового поля. URL-адрес для new_topic URL-адрес новой страницы должен быть простым и содержательным, поэтому после того, как пользователь выбрал команду создания новой темы, он направля- ется по адресу http://localhost:8000/new_topic/. Ниже приведена схема URL для страницы new_topic , которая добавляется в learning_logs/urls .py : urls.py urlpatterns = [ # Страница для добавления новой темы url(r'^new_topic/$', views.new_topic, name='new_topic'), ] Эта схема URL будет отправлять запросы функции представления new_topic() , которую мы сейчас напишем. 410 Глава 19 • Учетные записи пользователей Функция представления new_topic() Функция new_topic() должна обрабатывать две разные ситуации: исходные за- просы страницы new_topic (в этом случае должна отображаться пустая форма) и обработка данных, отправленных через форму. Затем она должна перенаправить пользователя обратно на страницу topics : views.py from django.shortcuts import render from django.http import HttpResponseRedirect from django.core.urlresolvers import reverse from .models import Topic from .forms import TopicForm def new_topic(request): """Определяет новую тему.""" if request.method != 'POST': # Данные не отправлялись; создается пустая форма. form = TopicForm() else: # Отправлены данные POST; обработать данные. form = TopicForm(request.POST) if form.is_valid(): form.save() return HttpResponseRedirect(reverse('learning_logs:topics')) context = {'form': form} return render(request, 'learning_logs/new_topic.html', context) Мы импортируем класс HttpResponseRedirect , который будет использоваться для перенаправления пользователя к странице topics после отправки введенной темы. Функция reverse() определяет URL по заданной схеме URL (то есть Django сгенерирует URL при запросе страницы). Также импортируется только что напи- санная форма TopicForm Запросы GET и POST При построении веб-приложений используются два основных типа запросов — GET и POST. Запросы GET используются для страниц, которые только читают данные с сервера, а запросы POST обычно используются в тех случаях, когда пользователь должен отправить информацию через форму. Для обработки всех наших форм будет использоваться метод POST (существуют и другие разновид- ности запросов, но в нашем проекте они не используются). Функция new_topic() получает в параметре объект запроса. Когда пользователь впервые запрашивает эту страницу, его браузер отправляет запрос GET. Когда пользователь уже заполнил и отправил форму, его браузер отправляет запрос POST. В зависимости от типа запроса мы определяем, запросил ли пользователь Редактирование данных 411 пустую форму (запрос GET) или предлагает обработать заполненную форму (за- прос POST). Метод запроса — GET или POST — проверяется в точке . Если метод запро- са отличен от POST, вероятно, используется запрос GET, поэтому необходимо вернуть пустую форму (даже если это запрос другого типа, это все равно безопас- но). Мы создаем экземпляр TopicForm , сохраняем его в переменной form и от- правляем форму шаблону в словаре context . Так как при создании TopicForm аргументы не передавались, Django создает пустую форму, которая заполняется пользователем. Если используется метод запроса POST, выполняется блок e l s e , кото- рый обрабатывает данные, отправленные в форме. Мы создаем экземпляр TopicForm и передаем ему данные, введенные пользователем, хранящиеся в request.POST . Возвращаемый объект form содержит информацию, отправлен- ную пользователем. Отправленную информацию нельзя сохранять в базе данных до тех пор, пока она не будет проверена . Функция is_valid() проверяет, что все обязательные поля были заполнены (все поля формы по умолчанию являются обязательными), а вве- денные данные соответствуют типам полей — например, что длина текста меньше 200 символов, как было указано в файле models .py в главе 18. Автоматическая про- верка избавляет нас от большого объема работы. Если все данные действительны, можно вызвать метод save() , который записывает данные из формы в базу дан- ных. После того как данные будут сохранены, страницу можно покинуть. Мы ис- пользуем вызов reverse() для получения URL-адреса страницы topics и передаем его функции HttpResponseRedirect() , перенаправляющей браузер пользователя на страницу topics . На этой странице пользователь видит только что введенную им тему в общем списке тем. Шаблон new_topic Теперь создадим новый шаблон с именем new_topic .html для отображения только что созданной формы: new_topic.html {% extends "learning_logs/base.html" %} {% block content %} Add a new topic: {% endblock content %} Этот шаблон расширяет base .html , поэтому он имеет такую же базовую структуру, как и остальные страницы Learning Log. В точке определяется форма HTML. Аргумент action сообщает серверу, куда передавать данные, отправленные фор- |