Этот шаблон расширяет base.html, поэтому он имеет такую же базовую структуру, как и остальные страницы Learning Log. В точке (1) определяется форма HTML. Аргумент action сообщает серверу, куда передавать данные, отправленные формой; в данном случае данные возвращаются функции представления new_topic(). Аргумент method приказывает браузеру отправить данные в запросе типа POST.
Django использует шаблонный тег {% csrf_token %} (2) для предотвращения попыток получения несанкционированного доступа к серверу (атаки такого рода называются межсайтовой подделкой запросов). В точке (3) отображается форма; это наглядный пример того, насколько легко в Django выполняются такие стандартные операции, как отображение формы. Чтобы автоматически создать все поля, необходимые для отображения формы, достаточно включить шаблонную переменную {{ form.as_p }}. Модификатор as_p приказывает Django отобразить все элементы формы в формате абзацев — это простой способ аккуратного отображения формы.
Django не создает кнопку отправки данных для форм, поэтому мы определяем ее в точке (4).
Создание ссылки на страницу new_topic
Далее ссылка на страницу new_topic создается на странице topics:
topics.html
{% extends "learning_logs/base.html" %}
{% block content %}
Topics
...
{% endblock content %}
Разместите ссылку после списка существующих тем. Полученная форма изображена на рис. 19.1. Воспользуйтесь ею и добавьте несколько своих тем.
Рис. 19.1. Страница для добавления новой темы
Добавление новых записей
Теперь, когда пользователь может добавлять новые темы, он также захочет добавлять новые записи. Мы снова определим URL, напишем новую функцию и шаблон и создадим ссылку на страницу. Но сначала нужно добавить в forms.py еще один класс.
Класс EntryForm
Мы должны создать форму, связанную с моделью Entry, но более специализированную по сравнению с TopicForm:
forms.py
from django import forms
from .models import Topic, Entry
class TopicForm(forms.ModelForm):
...
class EntryForm(forms.ModelForm):
. .class Meta:
. . . .model = Entry
. . . .fields = ['text']
(1) . . . .labels = {'text': ''}
(2) . . . .widgets = {'text': forms.Textarea(attrs={'cols': 80})}
Сначала в команду import к Topic добавляется Entry. Новый класс EntryForm наследует от forms.ModelForm и содержит вложенный класс Meta с указанием модели, на которой он базируется, и поле, включаемое в форму. Полю 'text' снова назначается пустая надпись (1) .
В точке (2) включается атрибут widgets. Виджет (widget) представляет собой элемент формы HTML: однострочное или многострочное текстовое поле, раскрывающийся список и т.д. Включая атрибут widgets, вы можете переопределить виджеты, выбранные Django по умолчанию. Приказывая Django использовать элемент forms.Textarea, мы настраиваем виджет ввода для поля 'text', чтобы ширина текстовой области составляла 80 столбцов вместо значения по умолчанию 40. У пользователя будет достаточно места для создания содержательных записей.
URL-адрес для new_entry
Необходимо включить аргумент topic_id в URL-адрес для создания новой записи, потому что запись должна ассоциироваться с конкретной темой. Вот как выглядит URL, который мы добавляем в learning_logs/urls.py:
urls.py
...
urlpatterns = [
...
. .# Страница для добавления новой записи
. .url(r'^new_entry/(?P
]
Эта схема URL соответствует любому URL-адресу в форме http://localhost:8000/new_entry/id/, где id — число, равное идентификатору темы. Выражение (?P
Функция представления new_entry()
Функция представления new_entry очень похожа на функцию добавления новой темы:
views.py
from django.shortcuts import render
...
from .models import Topic
from .forms import TopicForm, EntryForm
...
def new_entry(request, topic_id):
. ."""Добавляет новую запись по конкретной теме."""
(1) . .topic = Topic.objects.get(id=topic_id)
. .
(2) . .if request.method != 'POST':
. . . .# Данные не отправлялись; создается пустая форма.
(3) . . . .form = EntryForm() . . . .
. .else:
. . . .# Отправлены данные POST; обработать данные.
(4) . . . .form = EntryForm(data=request.POST)
. . . .if form.is_valid():
(5) . . . . . .new_entry = form.save(commit=False)
? . . . . . .new_entry.topic = topic
. . . . . .new_entry.save()
? . . . . . .return HttpResponseRedirect(reverse('learning_logs:topic',
. . . . . . . . . . . . . . . . . . . .args=[topic_id]))
. .
. .context = {'topic': topic, 'form': form}