Что такое формы в джанго
Перейти к содержимому

Что такое формы в джанго

  • автор:

Формы в Django

Последним, что нам стоит сделать для нашего веб-сайта, является удобный способ добавления и редактирования записей. admin -панель Django удобна, но её дизайн сложно изменять. С forms (формами) у нас будет абсолютная власть над интерфейсом блога — мы сможем сделать практически всё, что только можно придумать!

Формы Django удобны тем, что мы можем создать новую форму с нуля или воспользоваться ModelForm для сохранения содержимого формы в модель.

Это как раз то, что нам нужно сделать: мы создадим форму для модели Post .

Как и любая важная часть Django, формы имеют свой собственный файл: forms.py .

Нам нужно создать файл с таким именем в директории blog .

blog └── forms.py 

Теперь открой его и набери следующее:

from django import forms from .models import Post class PostForm(forms.ModelForm): class Meta: model = Post fields = ('title', 'text',) 

Для начала нам нужно импортировать формы Django ( from django import forms ) и, разумеется, нашу модель Post ( from .models import Post ).

PostForm , как ты, вероятно, подозреваешь, — это имя для нашей формы. Нам нужно также сообщить Django, что эта форма относится к ModelForm (чтобы он смог поколдовать для нас) — forms.ModelForm поможет с этим.

Дальше у нас class Meta , где мы определяем, какая модель будет использоваться для создания формы ( model = Post ).

В завершение мы можем указать, какие поля должны присутствовать в нашей форме. Сейчас нам требуются только поля title и text — author будет автоматически выбран в зависимости от авторизованного пользователя (тебя), а created_date должна автоматически проставляться в момент создания записи (т.е. через код), верно?

Вот и всё! Теперь мы можем использовать форму в представлении и отобразить её в шаблоне.

Поэтому снова нам необходимо создать ссылку на страницу, URL-адрес, представление и шаблон.

Ссылка на страницу с формой

Пришло время открыть файл blog/templates/blog/base.html . Мы добавим ссылку в элемент div с именем page-header :

a href="" class="top-menu">span class="glyphicon glyphicon-plus"> span> a> 

Обрати внимание, что мы назвали новое представление post_new . Класс glyphicon glyphicon-plus определён в используемой нами теме bootstrap — таким образом мы выведем значок плюса.

После добавления строки твой html-файл должен выглядеть следующим образом:

 html> head> title>Django Girls blog title> link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"> link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css"> link href='//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext' rel='stylesheet' type='text/css'> link rel="stylesheet" href=""> head> body> div class="page-header"> a href="" class="top-menu">span class="glyphicon glyphicon-plus"> span> a> h1>a href="/">Django Girls Blog a> h1> div> div class="content container"> div class="row"> div class="col-md-8">  div> div> div> body> html> 

После сохранения файла и перезагрузки страницы по адресу http://127.0.0.1:8000 ты, конечно, увидишь знакомую ошибку NoReverseMatch , верно?

URL

Нам нужно открыть файл blog/urls.py и добавить строку:

path('post/new/', views.post_new, name='post_new'), 

Окончательная версия файла будет выглядеть следующим образом:

from django.urls import path from . import views urlpatterns = [ path('', views.post_list, name='post_list'), path('post//', views.post_detail, name='post_detail'), path('post/new/', views.post_new, name='post_new'), ] 

После перезагрузки веб-сайта мы увидим ошибку AttributeError , поскольку представление post_new не реализовано. Давай добавим его прямо сейчас.

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

Самое время открыть файл blog/views.py и добавить следующую строку к остальным, начинающимся с from :

from .forms import PostForm 

А затем наше представление:

def post_new(request): form = PostForm() return render(request, 'blog/post_edit.html', 'form': form>) 

Чтобы создать новую форму Post , нам потребуется вызвать PostForm() и передать её шаблону. Мы ещё вернёмся к этому представлению, а пока давай быстро создадим шаблон под форму.

Шаблон

Нам нужно создать файл post_edit.html в директории blog/templates/blog . Чтобы заставить форму работать, нам потребуется несколько вещей:

  • Нам нужно отобразить форму. Мы можем сделать это, к примеру, простым > .
  • Строка выше должна быть обёрнута в HTML-теги
    .
  • Нам потребуется кнопка Save . Мы добавим её при помощи HTML-кнопки:
  • И, наконец, сразу после открытия тега < form. >мы должны добавить . Это очень важно, поскольку так мы делаем форму защищённой! Django будет ругаться, если ты забудешь добавить этот код:

Страница CSFR Forbidden

Хорошо, давай посмотрим, как должен выглядеть HTML-код в файле post_edit.html :

  h1>New post h1> form method="POST" class="post-form"> > button type="submit" class="save btn btn-default">Save button> form> 

Время обновить страницу! Ура! Форма отображается!

Новая форма

Но подожди минутку! Если ты наберёшь что-нибудь в полях title и text и попробуешь сохранить — что произойдёт?

Ничего! Мы снова на той же странице и наш текст пропал. и новая запись не была добавлена. Так что же пошло не так?

Ответ прост: ничего. Нам нужно сделать кое-что ещё, чтобы новое представление заработало.

Сохраняем данные из формы

Снова открой файл blog/views.py . Всё, что у нас есть в представлении post_new , выглядит пока следующим образом:

def post_new(request): form = PostForm() return render(request, 'blog/post_edit.html', 'form': form>) 

После отправки формы мы возвращаемся к тому же представлению, но в этот раз с новыми данными в request , а точнее, в request.POST (имя POST не имеет ничего общего с «постом» в блоге, оно связано с тем, что мы «публикуем» данные). Помнишь, что в HTML-файле определение имеет параметр method=»POST» ? Все поля формы теперь находятся в request.POST . Ты не должна переименовывать POST во что-то другое (другое доступное значение параметра method — GET , но у нас сейчас нет времени объяснять разницу).

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

if request.method == "POST": [. ] else: form = PostForm() 

Теперь заполним строку, занятую [. ] . Если method — POST , тогда мы хотим построить PostForm с данными из формы, верно? Мы добьёмся этого следующим образом:

form = PostForm(request.POST) 

Легко! Дальше мы проверим, корректна ли форма (все ли необходимые поля заполнены и не отправлено ли некорректных значений). Мы сделаем это при помощи form.is_valid() .

Мы проверяем, допустимо ли содержимое формы, и, если всё в порядке, сохраняем её!

if form.is_valid(): post = form.save(commit=False) post.author = request.user post.published_date = timezone.now() post.save() 

Фактически мы выполняем две операции: сохраняем форму form.save и добавляем автора (поскольку обязательного поля author нет в PostForm !). commit=False означает, что мы пока не хотим сохранять модель Post — сначала нужно добавить автора. В основном ты будешь использовать form.save() , без commit=False , но в данном случае нам это пригодится. post.save() сохранит изменения (после добавления автора), и новая запись будет создана!

Наконец, будет прекрасно, если мы сможем сразу переходить к странице post_detail после добавления новой записи, согласна? Для этого нам понадобится еще один импорт:

from django.shortcuts import redirect 

Добавь эту строку в начало файла. Теперь мы можем сделать переадресацию на страницу post_detail для созданной записи:

return redirect('post_detail', pk=post.pk) 

post_detail — это имя представления, которое нам необходимо. Помнишь, что это представление требует переменную pk ? Чтобы передать её представлению, мы используем аргумент pk=post.pk , где post — это новая запись в блоге!

Хорошо, мы многое обсудили, пора взглянуть на представление полностью, верно?

def post_new(request): if request.method == "POST": form = PostForm(request.POST) if form.is_valid(): post = form.save(commit=False) post.author = request.user post.published_date = timezone.now() post.save() return redirect('post_detail', pk=post.pk) else: form = PostForm() return render(request, 'blog/post_edit.html', 'form': form>) 

Проверим, всё ли работает. Перейди по адресу http://127.0.0.1:8000/post/new/, добавь текст в поля title и text , затем сохрани… и вуаля! Новая запись создана, и мы перешли на страницу post_detail !

Возможно, ты заметила, что мы устанавливаем дату публикации перед сохранением поста. В последствии мы сделаем кнопку публикации в Django Girls Tutorial: Extensions.

Поскольку мы недавно использовали панель администратора Django, система до сих пор думает, что мы авторизованы. Существует несколько случаев, когда мы можем случайно выйти из аккаунта (закрытие браузера, перезапуск базы данных и т.д.). Если ты получаешь ошибку при попытке сохранения записи, то потребуется перейти на страницу http://127.0.0.1:8000/admin и авторизоваться в системе снова. Это решит проблему. В главе Домашнее задание: добавляем безопасность нашему веб-сайту! после основного учебника описано окончательное решение этой проблемы.

Ошибка при выходе из аккаунта

Валидация формы

Теперь мы покажем тебе, насколько круты формы в Django. Запись в блоге должна иметь поля title и text . В нашей модели Post мы не указываем, что эти поля необязательны (в отличие от published_date ), так что Django по умолчанию будет ожидать, что пользователь их заполнит.

Попробуй сохранить форму с незаполненными полями title и text . Угадай, что произойдёт!

Валидация формы

Django заботится о проверке всех полей в нашей форме на корректность. Разве не шикарно?

Форма редактирования

Теперь мы знаем, как добавить новую форму. Но что, если мы хотим внести исправления в уже существующую запись? По сути это схожая с предыдущей задача. Давай быстро создадим пару важных вещей (если ты чего-то не понимаешь, спроси своего тренера или загляни в предыдущие главы, поскольку мы уже объясняли все необходимые шаги).

Открой blog/templates/blog/post_detail.html и добавь следующую строку:

a class="btn btn-default" href="">span class="glyphicon glyphicon-pencil"> span> a> 

так, чтобы шаблон выглядел следующим образом:

  div class="post"> div class="date"> > div> a class="btn btn-default" href="">span class="glyphicon glyphicon-pencil"> span> a> h1>> h1> p>> p> div> 

В файле blog/urls.py добавь:

 path('post//edit/', views.post_edit, name='post_edit'), 

Мы будем использовать повторно шаблон blog/templates/blog/post_edit.html , так что осталось лишь отсутствующее представление.

Давай откроем файл blog/views.py и добавим в самый конец следующее:

def post_edit(request, pk): post = get_object_or_404(Post, pk=pk) if request.method == "POST": form = PostForm(request.POST, instance=post) if form.is_valid(): post = form.save(commit=False) post.author = request.user post.published_date = timezone.now() post.save() return redirect('post_detail', pk=post.pk) else: form = PostForm(instance=post) return render(request, 'blog/post_edit.html', 'form': form>) 

Выглядит практически идентично представлению post_new , верно? Но не совсем. Во-первых, мы передаём параметр pk из URL-адреса. Кроме того, мы получаем модель Post для редактирования при помощи get_object_or_404(Post, pk=pk) и передаём экземпляр post в качестве instance форме и при сохранении…

form = PostForm(request.POST, instance=post) 

… и когда мы открываем форму для редактирования:

form = PostForm(instance=post) 

Хорошо, давай удостоверимся, что всё работает! Перейди на страницу post_detail . Ты должна увидеть кнопку редактирования в правом верхнем углу:

Кнопка Редактировать (Edit)

Когда ты её нажмёшь, то увидишь форму с выбранной записью:

Форма редактирования

Поменяй заголовок и текст, а затем сохрани запись!

Поздравляем! Твое приложение становится всё более сложным!

Если тебе нужно больше информации о формах в Django, обратись к официальной документации: https://docs.djangoproject.com/en/2.0/topics/forms/

Безопасность

Круто иметь возможность создавать новые посты, просто перейдя по ссылке! Однако сейчас кто угодно из посетителей твоего сайта может создать новую запись в блоге, а это, скорее всего, совсем не то, чего бы тебе хотелось. Давай сделаем так, чтобы кнопка показывалась для нас, а не кого-либо ещё.

В файле blog/templates/blog/base.html найди page-header div и тег , который мы добавили ранее. Должно выглядеть примерно так:

a href="" class="top-menu">span class="glyphicon glyphicon-plus"> span> a> 

Мы добавим сюда ещё один тег , чтобы ссылка показывалась только пользователям, вошедшим в админку. То есть пока что — только тебе! Измени тег , чтобы получилось так:

 a href="" class="top-menu">span class="glyphicon glyphicon-plus"> span> a> 

Из-за этого ссылка будет отправлена в браузер, только если запрашивающий страницу пользователь вошёл в систему. Это не обезопасит создание новых постов полностью, но для начала и это неплохо. Мы подробнее рассмотрим вопросы безопасности в дополнении к учебнику.

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

Открой файл blog/templates/blog/post_detail.html и найди такую строку:

a class="btn btn-default" href="">span class="glyphicon glyphicon-pencil"> span> a> 

Замени её на следующее:

 a class="btn btn-default" href="">span class="glyphicon glyphicon-pencil"> span> a> 

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

И последнее: публикация!

Теперь давай посмотрим, как это будет работать на PythonAnywhere. Пришло время для очередного развёртывания!

  • Сначала нам нужно сделать commit и push нового кода в репозиторий GitHub:
$ git status $ git add --all . $ git status $ git commit -m "Added views to create/edit blog post inside the site." $ git push 
  • Затем набери в Bash-консоли PythonAnywhere:
$ cd ~/.pythonanywhere.com $ git pull [. ] 

(Не забудь подставить вместо свой поддомен на PythonAnywhere без угловых скобок.)

  • Наконец, зайди на страницу «Web» page (используй кнопку меню в правом верхнем углу консоли) и нажми Reload. Обнови страницу блога http://subdomain.pythonanywhere.com/, чтобы увидеть изменения.

Вот и всё! Поздравляем 🙂

Что такое формы в джанго

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

Каждая форма определяется в виде отдельного класса, который расширяет класс forms.Form . Классы размещаются внутри проекта, где они используются. Нередко они помещаются в отдельный файл, который называется, к примеру, forms.py . Однако также формы могут размещаться внутри уже имеющихся в приложении файлов, например, в views.py или models.py .

Например, создадим в приложении новый файл forms.py и поместим в него следующий код:

from django import forms class UserForm(forms.Form): name = forms.CharField() age = forms.IntegerField()

Класс формы называется UserForm. Он определяет два поля. Поле name представляет тип forms.CharField и будет генерировать поле input type=»text» . Поле age представляет тип forms.IntegerField и будет генерировать поле input type=»number» . То есть первое поле для ввода текста, а второе для ввода чисел.

Определение форм Django и класс forms.Form

Далее в файле views.py определим следующее представление:

from django.shortcuts import render from .forms import UserForm def index(request): userform = UserForm() return render(request, "index.html", )

Здесь объект формы передается в шаблон index.html в виде переменной form.

В файле urls.py пропишем маршрут для этого представления:

from django.urls import path from hello import views urlpatterns = [ path("", views.index), ]

И определим следующий шаблон index.html :

    METANIT.COM   >

В итоге из переменной form будет генерироваться следующий код html:

  

Формы в Django

Получение POST-запроса

Теперь с помощью форм Django определим полнофункциональную форму, с которой можно отправлять данные на сервер.

Вначале изменим шаблон index.html :

    METANIT.COM     >

Для создания формы здесь использован стандартный элемент html . В начале формы помещен встроенный тег Django , который позволяет защитить приложение от CSRF-атак, добавляя в форму в виде скрытого поля csrf-токен.

Внизу формы определена кнопка для отправки данной формы на сервер.

Теперь изменим представление в файле views.py :

from django.shortcuts import render from django.http import HttpResponse from .forms import UserForm def index(request): if request.method == "POST": name = request.POST.get("name") age = request.POST.get("age") return HttpResponse(f"Привет, , твой возраст: ") else: userform = UserForm() return render(request, "index.html", )

Поскольку в шаблоне форма по умолчанию будет отправляться на тот же адрес, то представление обрабатывает сразу два типа запросов GET и POST. Для определения типа запроса проверяем значение request.method .

Если запрос типа POST, то вначале создаем объект UserForm, наполняя его данными, которые пришли в запросе через request.POST. То есть это и будут отправленные данные формы. Затем мы можем получить эти данные по отдельности для каждого поля формы. После этого отправляем пользователю сообещние через объект HttpResponse. В принципе тут можно было бы сделать переадресацию или использовать другой шаблон для генерации ответа.

Если запрос представляет тип GET, то просто отправляем форму для ввода данных.

Таким образом, при обращении к приложению мы вначале увидим форму ввода. Введем в нее некоторые данные:

Обработка ввода в формах Django

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

Руководство часть 9: Работа с формами

На этом уроке мы покажем вам процесс работы с HTML-формами в Django. В частности, продемонстрируем самый простой способ построения формы для создания, обновления и удаления экземпляров модели. При этом мы расширим сайт местной библиотеки, чтобы библиотекари могли обновлять книги, создавать, обновлять и удалять авторов, используя наши собственные формы (а не возможности приложения администратора).

Необходимые условия: Завершите все предыдущие учебные темы, в том числе Django руководство часть 8: Аутентификация пользователя и права доступа (en-US) .
Цель: Научиться понимать, как создавать формы, чтобы получать информацию от пользователей и обновлять базу данных. Узнать, как обобщённые классы отображения форм могут значительно упростить процесс создания форм при работе с одной моделью.

Обзор

HTML форма — это группа из одного или нескольких полей/виджетов на веб-странице, которая используется для сбора информации от пользователей для последующей отправки на сервер. Формы являются гибким механизмом сбора пользовательских данных, поскольку имеют целый набор виджетов для ввода различных типов данных, как то: текстовые поля, флажки, переключатели, установщики дат и т. д. Формы являются относительно безопасным способом взаимодействия пользовательского клиента и сервера, поскольку они позволяют отправлять данные в POST-запросах, применяя защиту от Межсайтовой подделки запроса (Cross Site Request Forgery — CSRF)

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

Admin Site - Book Add

Работа с формами может быть достаточно сложной! Разработчикам надо описать форму на HTML, проверить её валидность, а также, на стороне сервера, проверять введённые пользователем данные (а возможно и на стороне клиента), далее, в случае возникновения ошибок необходимо опять показать пользователю форму и, при этом, указать на то, что пошло не так, в случае же успеха проделать с данными необходимые операции и каким-то образом проинформировать об этом пользователя. Django, при работе с формами, берёт большую часть, описанной выше работы, на себя. Он предоставляет фреймворк, который позволяет вам определять форму и её поля программно, а затем использовать эти объекты и для генерации непосредственно кода HTML-формы, и для контроля за процессом валидации и других пользовательский взаимодействий с формой.

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

Формы HTML

Начнём мы с краткого обзора Форм HTML. Рассмотрим простую форму HTML, имеющую поле для ввода имени некоторой «команды» («team»), и, связанную с данным полем, текстовой меткой:

Simple name field example in HTML form

Форма описывается на языке HTML как набор элементов, расположенных внутри парных тэгов

.

. Любая форма содержит как минимум одно поле-тэг input типа type=»submit» .

form action="/team_name_url/" method="post"> label for="team_name">Enter name: label> input id="team_name" type="text" name="name_field" value="Default name for team." /> input type="submit" value="OK" /> form> 

Здесь у нас только одно поле для ввода имени команды, но форма может иметь любое количество элементов ввода и, связанных с ними, текстовых меток. Атрибут элемента type определяет какого типа виджет будет показан в данной строке. Атрибуты name и id используются для однозначной идентификации данного поля в JavaScript/CSS/HTML, в то время как value содержит значение для поля (когда оно показывается в первый раз). Текстовая метка добавляется при помощи тэга label (смотрите «Enter name», в предыдущем фрагменте) и имеет атрибут for со значением идентификатора id , того поля, с которым данная текстовая метка связана.

Элемент input с type=»submit» будет показана как кнопка (по умолчанию), нажав на которую, пользователь отправляет введённые им данные на сервер (в данном случае только значение поля с идентификатором team_name ). Атрибуты формы определяют каким методом будут отправлены данные на сервер (атрибут method ) и куда (атрибут action ):

  • action : Это ресурс/URL-адрес куда будут отправлены данные для обработки. Если значение не установлено (то есть, значением поля является пустая строка), тогда данные будут отправлены в отображение (функцию, или класс), которое сформировало текущую страницу.
  • method : HTTP-метод, используемый для отправки данных: post, или get.
    • Метод POST должен всегда использоваться если отправка данных приведёт к внесению изменений в базе данных на сервере. Применение данного метода должно повысить уровень защиты от CSRF.
    • Метод GET должен применяться только для форм, действия с которыми не приводят к изменению базы данных (например для поисковых запросов). Кроме того, данный метод рекомендуется применять для создания внешних ссылок на ресурсы сайта.

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

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

    Процесс управления формой в Django

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

    Диаграмма, представленная ниже, демонстрирует процесс работы с формой в Django, начиная с запроса страницы, содержащей форму (выделено зелёным цветом).

    Updated form handling process doc.

    В соответствии с данной диаграммой, главными моментами, которые берут на себя формы Django являются:

    1. Показ формы по умолчанию при первом запросе со стороны пользователя.
      • Форма может содержать пустые поля (например, если вы создаёте новую запись в базе данных), или они (поля) могут иметь начальные значения (например, если вы изменяете запись, или хотите заполнить её каким-либо начальным значением).
      • Форма в данный момент является несвязанной, потому что она не ассоциируется с какими-либо введёнными пользователем данными (хотя и может иметь начальные значения).
    2. Получение данных из формы (из HTML-формы) со стороны клиента и связывание их с формой (классом формы) на стороне сервера.
      • Связывание данных с формой означает, что данные, введённые пользователем, а также возможные ошибки, при переотрисовке в дальнейшем, будут относиться именно к данной форме, а не к какой-либо ещё.
    3. Очистка и валидация данных.
      • Очистка данных — это их проверка на наличие возможных значений, или вставок в поля ввода (то есть очистка — это удаление неправильных символов, которые потенциально могут использоваться для отправки вредоносного содержимого на сервер), с последующей конвертацией очищенных данных в подходящие типы данных Python.
      • Валидация проверяет, значения полей (например, правильность введённых дат, их диапазон и так далее)
    4. Если какие-либо данные являются неверными, то выполнение перерисовки формы, но на этот раз, с уже введёнными пользователем данными и сообщениями об ошибках, описывающих возникшие проблемы.
    5. Если все данные верны, то исполнение необходимых действий (например, сохранение данных, отправка писем, возврат результата поиска, загрузка файла и так далее)
    6. Когда все действия были успешно завершены, то перенаправление пользователя на другую страницу.

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

    Примечание: Понимание того, как используется класс Form поможет вам когда мы будем рассматривать классы фреймворка Django, для работы с формами более «высокого уровня».

    HTML-форма обновления книги. Класс Form и функция отображения

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

    Данный пример будет использовать отображение на основе функции, а также продемонстрирует работу с классом Form . Следующие разделы покажут изменения, которые вам надо сделать, чтобы продемонстрировать работу форм в проекте LocalLibrary.

    Класс Form

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

    Объявление класса формы Form

    Синтаксис объявления для класса формы Form очень похож на объявление класса модели Model , он даже использует те же типы полей (и некоторые похожие параметры). Это существенный момент, поскольку в обоих случаях нам надо убедиться, что каждое поле управляет правильным типом данных, соответствует нужному диапазону (или другому критерию) и имеет необходимое описание для показа/документации.

    Для того, чтобы создать класс с возможностями базового класса Form мы должны импортировать библиотеку forms , наследовать наш класс от класса Form , а затем объявить поля формы. Таким образом, самый простой класс формы в нашем случае будет иметь вид, показанный ниже:

    from django import forms class RenewBookForm(forms.Form): renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).") 
    Поля формы

    В нашем случае мы имеем одно поле типа DateField , которое служит для ввода обновлённой даты возврата книги, которое будет отрендерено в HTML с пустым значением и текстовой меткой «Renewal date:«, а также текстовым описанием: «Enter a date between now and 4 weeks (default 3 weeks).» Так как никаких дополнительных опций мы не определяем, то поле будет «получать» даты в следующем формате input_formats: YYYY-MM-DD (2016-11-06), MM/DD/YYYY (02/26/2016), MM/DD/YY (10/25/16), а для отрисовки по умолчанию, будет использовать виджет: DateInput.

    Общие аргументы для большинства полей перечислены ниже:

    • required: Если True , то данное поле не может быть пустым, или иметь значение None . Данное значение установлено по умолчанию.
    • label: Текстовая метка, используемая для рендеринга поля в HTML-код. Если label не определена, то Django попытается создать её значение при помощи имени поля, переводя первый символ в верхний регистр, а также заменяя символы подчёркивания пробелами (например, для переменной с именем renewaldate, будет создан следующий текст метки: _Renewal date).
    • label_suffix: По умолчанию показывает двоеточие после текста метки (например, Renewal date**:**). Данный параметр позволяет вам указать любой суффикс по вашему желанию.
    • initial: Начальное значение для поля при показе формы.
    • widget: Применяемый виджет для поля.
    • help_text (как показано в примере выше): Дополнительный текст, который может быть показан на форме, для описания того, как использовать поле.
    • error_messages: Список сообщений об ошибках для данного поля. Вы можете переопределить его своими сообщениями, при необходимости.
    • validators: Список функций, которые будут вызваны для валидации, введённого в поле значения.
    • localize: Позволяет осуществить локализацию данных поля формы (например, формат ввода числовых значений, или дат).
    • disabled: Если установлено в True , то поле показывается, но его значение изменить нельзя. По умолчанию равно False .
    Валидация

    Django предоставляет несколько мест где вы можете осуществить валидацию ваших данных. Простейшим способом проверки значения одиночного поля является переопределение метода clean_() (здесь, это имя поля, которое вы хотите проверить). Например, мы хотим проверить, что введённое значение renewal_date находится между текущей датой и 4 неделями в будущем. Для этого мы создаём метод clean_renewal_date() , как показано ниже:

    from django import forms from django.core.exceptions import ValidationError from django.utils.translation import ugettext_lazy as _ import datetime #for checking renewal date range. class RenewBookForm(forms.Form): renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).") def clean_renewal_date(self): data = self.cleaned_data['renewal_date'] #Проверка того, что дата не выходит за "нижнюю" границу (не в прошлом). if data  datetime.date.today(): raise ValidationError(_('Invalid date - renewal in past')) #Проверка того, то дата не выходит за "верхнюю" границу (+4 недели). if data > datetime.date.today() + datetime.timedelta(weeks=4): raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead')) # Помните, что всегда надо возвращать "очищенные" данные. return data 

    Необходимо отметить два важных момента. Первый это то, что мы получаем наши данные при помощи словаря self.cleaned_data[‘renewal_date’] , а затем в конце возвращаем полученное значение, для проведения необходимых проверок. Данный шаг позволяет нам, при помощи валидаторов, получить «очищенные», проверенные, а затем, приведённые к стандартным типам, данные (в нашем случае к типу Python datetime.datetime ).

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

    Примечание: Существует множество других методов и примеров валидации различных форм, которые можно найти в Формы и валидация поля (Django docs). Например, в случае, если у вас имеется много полей, которые зависят один от другого, вы можете переопределить функцию Form.clean() и, при необходимости, «выкинуть» ValidationError .

    В целом, это все, что нам понадобится для создания формы в данном примере!

    Копирование класса формы

    Создайте и откройте файл locallibrary/catalog/forms.py, а затем скопируйте в него весь код, указанный в предыдущем фрагменте.

    Конфигурация URL-адресов

    Перед созданием отображения давайте добавим соответствующую конфигурацию URL-адреса для страницы обновления книг. Скопируйте следующий фрагмент в нижнюю часть файла locallibrary/catalog/urls.py.

    += [ url(r'^book/(?P[-\w]+)/renew/$', views.renew_book_librarian, name='renew-book-librarian'), ] 

    Данная конфигурация перенаправит запросы с адресов формата /catalog/book/ /renew/ в функции с именем renew_book_librarian() в views.py, туда же передаст идентификатор id записи BookInstance в качестве параметра с именем pk . Шаблон соответствует только если pk это правильно отформатированный uiid.

    Примечание: Вместо имени «pk» мы можем использовать любое другое, по нашему желанию, потому что мы имеем полный контроль над функцией отображения (которого у нас нет в случае использования встроенного обобщённого класса отображения, который ожидает параметр с определённым именем). Тем не менее имя pk является понятным сокращением от «primary key», поэтому мы его тут и используем!

    Отображение

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

    Для форм, которые используют POST -запрос при отправке информации на сервер, наиболее общей схемой проверки данного факта является следующая строка кода if request.method == ‘POST’: . GET -запросу, а также первому запросу формы, в таком случае соответствует блок else . Если вы хотите отправлять свои данные в виде GET -запроса, то в таком случае приёмом проверки того факта, что данный запрос первый (или последующий), является получение значения какого-либо поля формы (например, если значение скрытого поля формы пустое, то данный вызов является первым).

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

    from django.shortcuts import get_object_or_404 from django.http import HttpResponseRedirect from django.urls import reverse import datetime from .forms import RenewBookForm def renew_book_librarian(request, pk): book_inst = get_object_or_404(BookInstance, pk=pk) # Если данный запрос типа POST, тогда if request.method == 'POST': # Создаём экземпляр формы и заполняем данными из запроса (связывание, binding): form = RenewBookForm(request.POST) # Проверка валидности данных формы: if form.is_valid(): # Обработка данных из form.cleaned_data #(здесь мы просто присваиваем их полю due_back) book_inst.due_back = form.cleaned_data['renewal_date'] book_inst.save() # Переход по адресу 'all-borrowed': return HttpResponseRedirect(reverse('all-borrowed') ) # Если это GET (или какой-либо ещё), создать форму по умолчанию. else: proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3) form = RenewBookForm(initial='renewal_date': proposed_renewal_date,>) return render(request, 'catalog/book_renew_librarian.html', 'form': form, 'bookinst':book_inst>) 

    В первую очередь мы импортируем наш класс формы ( RenewBookForm ), а также другие необходимые объекты и методы:

    • get_object_or_404() : Возвращает определённый объект из модели в зависимости от значения его первичного ключа, или выбрасывает исключение Http404 , если данной записи не существует.
    • HttpResponseRedirect : Данный класс перенаправляет на другой адрес (HTTP код статуса 302).
    • reverse() : Данная функция генерирует URL-адрес при помощи соответствующего имени URL конфигурации/преобразования и дополнительных аргументов. Это эквивалент Python тэгу url , которые мы использовали в наших шаблонах.
    • datetime : Библиотека Python для работы с датами и временим.

    В отображении аргумент pk мы используем в функции get_object_or_404() для получения текущего объекта типа BookInstance (если его не существует, то функция, а следом и наше отображение прервут своё выполнение, а на странице пользователя отобразится сообщение об ошибке: «объект не найден»). Если запрос вызова отображения не является POST -запросом, то мы переходим к условному блоку else , в котором мы создаём форму по умолчанию и передаём ей начальное значения initial для поля renewal_date (выделено жирным ниже, — 3 недели, начиная с текущей даты).

    = get_object_or_404(BookInstance, pk=pk) # Если это GET (или другой метод), тогда создаём форму по умолчанию else: proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3) form = RenewBookForm(initial='renewal_date': proposed_renewal_date,>) return render(request, 'catalog/book_renew_librarian.html', 'form': form, 'bookinst':book_inst>) 

    После создания формы мы вызываем функцию render() , чтобы создать HTML страницу; передаём ей в качестве параметров шаблон и контекст, который содержит объект формы. Кроме того, контекст содержит объект типа BookInstance , который мы будем использовать в шаблоне, для получения информации об обновляемой книге.

    Если все таки у нас POST -запрос, тогда мы создаём объект с именем form и заполняем его данными, полученными из запроса. Данный процесс называется связыванием (или, биндингом, от англ. «binding») и позволяет нам провести валидацию данных. Далее осуществляется валидация формы, при этом проверяются все поля формы — для этого используются как код обобщённого класса, так и пользовательских функций, в частности нашей функции проверки введённых дат clean_renewal_date() .

    = get_object_or_404(BookInstance, pk=pk) # Если данный запрос типа POST, тогда if request.method == 'POST': # Создаём экземпляр формы и заполняем данными из запроса (связывание, binding): form = RenewBookForm(request.POST) # Проверка валидности формы: if form.is_valid(): # process the data in form.cleaned_data as required (here we just write it to the model due_back field) book_inst.due_back = form.cleaned_data['renewal_date'] book_inst.save() # redirect to a new URL: return HttpResponseRedirect(reverse('all-borrowed') ) return render(request, 'catalog/book_renew_librarian.html', 'form': form, 'bookinst':book_inst>) 

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

    Если форма прошла валидацию, тогда мы можем начать использовать данные, получая их из атрибута формы form.cleaned_data (то есть, data = form.cleaned_data[‘renewal_date’] ). Здесь мы просто сохраняем данные в поле due_back , соответствующего объекта BookInstance .

    Предупреждение: Важно: Хотя вы также можете получить доступ к данным формы непосредственно через запрос (например request.POST[‘renewal_date’], или request.GET[‘renewal_date’] (в случае GET-запроса), это НЕ рекомендуется. Очищенные данные проверены на вредоносность и преобразованы в типы, совместимые с Python.

    Последним шагом в части обработки формы представления является перенаправление на другую страницу, обычно страницу «Успех». В нашем случае мы используем объект класса HttpResponseRedirect и функцию reverse() для перехода к отображению с именем ‘all-borrowed’ (это было домашним заданием в Руководство часть 8: Аутентификация и разграничение доступа (en-US) ). Если вы не создали данную страницу, то просто укажите переход на домашнюю страницу сайта по адресу ‘/’).

    Все это необходимо для управления формой как таковой, но нам нужно как-то ограничить доступ к отображению (открыть доступ только библиотекарям). Мы могли бы создать новое разрешение (permission) в классе BookInstance (» can_renew «), но мы пойдём простым путём и воспользуемся функцией-декоратором @permission_required вместе с нашим существующим разрешением can_mark_returned .

    Окончательный вид отображения показан ниже. Пожалуйста, скопируйте данный текст в нижнюю часть файла locallibrary/catalog/views.py.

    from django.contrib.auth.decorators import permission_required from django.shortcuts import get_object_or_404 from django.http import HttpResponseRedirect from django.urls import reverse import datetime from .forms import RenewBookForm @permission_required('catalog.can_mark_returned') def renew_book_librarian(request, pk): """ View function for renewing a specific BookInstance by librarian """ book_inst = get_object_or_404(BookInstance, pk=pk) # If this is a POST request then process the Form data if request.method == 'POST': # Create a form instance and populate it with data from the request (binding): form = RenewBookForm(request.POST) # Check if the form is valid: if form.is_valid(): # process the data in form.cleaned_data as required (here we just write it to the model due_back field) book_inst.due_back = form.cleaned_data['renewal_date'] book_inst.save() # redirect to a new URL: return HttpResponseRedirect(reverse('all-borrowed') ) # If this is a GET (or any other method) create the default form. else: proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3) form = RenewBookForm(initial=) return render(request, 'catalog/book_renew_librarian.html', )

    Шаблон

    Создайте шаблон, на который ссылается наше отображение (/catalog/templates/catalog/book_renew_librarian.html) и скопируйте в него код, указанный ниже:

    h1>Renew: >h1> p>Borrower: >p> >Due date: >p> form action="" method="post"> table> > table> input type="submit" value="Submit" /> form> 

    Большая его часть вам знакома из предыдущих частей руководства. Мы расширяем базовый шаблон, а затем замещаем блок содержимого content . У нас имеется возможность ссылаться на переменную > (и её поля) поскольку мы передали её в объект контекста при вызове функции render() . Здесь мы используем данный объект для вывода заголовка книги, дат её получения и возврата.

    Код формы относительно прост. В первую очередь мы объявляем тэг form , затем определяем куда будут отправлены данные ( action ) и каким способом ( method , в данном случае «HTTP POST») — если обратитесь к обзору раздела Формы HTML в верхней части данной страницы, то найдёте там замещение, что пустое значение атрибута action , означает, что данные из формы будут переданы обратно по текущему URL-адресу данной страницы (чего мы и хотим!). Внутри тэга формы мы объявляем кнопку submit при помощи которой мы можем отправить наши данные. Блок , добавленный первой строкой внутри блока формы, является частью фреймворка Django и служит для борьбы с CSRF.

    Примечание: Добавляйте в каждый шаблон Django, в котором вы создаёте форму для отправки данных методом POST . Это поможет уменьшить вероятность взлома вашего сайта злоумышленниками.

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

    tr> th>label for="id_renewal_date">Renewal date:label>th> td> input id="id_renewal_date" name="renewal_date" type="text" value="2016-11-08" required /> br /> span class="helptext" >Enter date between now and 4 weeks (default 3 weeks).span > td> tr> 

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

    Если вы ввели неправильную дату, то на странице вы должны получить список сообщений об ошибках (показано жирным ниже).

    tr> th>label for="id_renewal_date">Renewal date:label>th> td> ul class="errorlist"> li>Invalid date - renewal in pastli> ul> input id="id_renewal_date" name="renewal_date" type="text" value="2015-11-08" required /> br /> span class="helptext" >Enter date between now and 4 weeks (default 3 weeks).span > td> tr> 
    Другие варианты применения переменной шаблона form

    В простом случае применения > как показано выше, каждое поле рендерится в виде отдельной строки таблицы. Кроме того, вы можете отрендерить каждое поле как список элементов ( > ), или как параграф ( > ).

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

    Примеры того как вручную отрендерить формы в шаблонах, а также пробежать циклом по шаблонным полям, смотрите Работы с формами > Ручная работа с формами (Django docs).

    Тестирование страницы

    Если вы выполнили задание в Django руководство часть 8: Аутентификация и разрешение доступа (en-US) , то у вас должна быть страница со списком всех книг в наличии библиотеки и данный список (страница) должен быть доступен только её сотрудникам. На данной странице в каждом пункте (для каждой книги) мы можем добавить ссылку на нашу новую страницу обновления книги.

     if perms.catalog.can_mark_returned %>- a href=" url 'renew-book-librarian' bookinst.id %>">Renewa>  endif %> 

    Примечание: Помните что, для того чтобы перейти на страницу обновления книги, ваш тестовый логин должен иметь разрешение доступа типа » catalog.can_mark_returned «(возможно надо воспользоваться вашим аккаунтом для суперпользователя).

    Вы можете попробовать вручную создать URL-адрес для тестирования, например — http://127.0.0.1:8000/catalog/book//renew/ (правильный идентификатор записи id для bookinstance можно получить, если перейти на страницу детальной информации книги и скопировать поле id ).

    Как теперь все это выглядит?

    Если все получилось как надо, то форма по умолчанию должна выглядеть следующим образом:

    А такой наша форма будет в случае ввода неправильной даты:

    Список всех книг с ссылками на страницу обновления данных:

    Класс ModelForm

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

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

    Базовая реализация ModelForm содержит тоже поле как и ваш предыдущий класс формы RenewBookForm , что и показано ниже. Все что вам необходимо сделать, — внутри вашего нового класса добавить класс Meta и связать его с моделью model ( BookInstance ), а затем перечислить поля модели в поле fields которые должны быть включены в форму (вы можете включить все поля при помощи fields = ‘__all__’ , или можно воспользоваться полем exclude (вместо fields ), чтобы определить поля модели, которые не нужно включать).

    from django.forms import ModelForm from .models import BookInstance class RenewBookModelForm(ModelForm): class Meta: model = BookInstance fields = ['due_back',] 

    Примечание: Это не выглядит сильно проще, чем просто использовать класс Form (и это действительно так, поскольку мы используем только одно поле). Тем не менее, если вы хотите иметь много полей, то такой способ построения формы может значительно уменьшить количество кода и ускорить разработку!

    Оставшаяся часть информации касается объявления полей модели (то есть, текстовых меток, виджетов, текстов, сообщений об ошибках). Если они недостаточно «правильные», то тогда мы можем переопределить их в нашем классе Meta при помощи словаря, содержащего поле, которое надо изменить и его новое значение. Например, в нашей форме мы могли бы поменять текст метки для поля «Renewal date» (вместо того, чтобы оставить текст по умолчанию: Due date), а кроме того мы хотим написать другой вспомогательный текст. Класс Meta , представленный ниже, показывает вам, как переопределить данные поля. Кроме того, при необходимости, вы можете установить значения для виджетов widgets и сообщений об ошибках error_messages .

    class Meta: model = BookInstance fields = ['due_back',] labels =  'due_back': _('Renewal date'), > help_texts =  'due_back': _('Enter a date between now and 4 weeks (default 3).'), > 

    Чтобы добавить валидацию, вы можете использовать тот же способ как и для класса Form — вы определяете функцию с именем clean_field_name() из которой выбрасываете исключение ValidationError , если это необходимо. Единственным отличием от нашей оригинальной формы будет являться то, что поле модели имеет имя due_back , а не » renewal_date «.

    from django.forms import ModelForm from .models import BookInstance class RenewBookModelForm(ModelForm): def clean_due_back(self): data = self.cleaned_data['due_back'] #Проверка того, что дата не в прошлом if data  datetime.date.today(): raise ValidationError(_('Invalid date - renewal in past')) #Check date is in range librarian allowed to change (+4 weeks) if data > datetime.date.today() + datetime.timedelta(weeks=4): raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead')) # Не забывайте всегда возвращать очищенные данные return data class Meta: model = BookInstance fields = ['due_back',] labels =  'due_back': _('Renewal date'), > help_texts =  'due_back': _('Enter a date between now and 4 weeks (default 3).'), > 

    Теперь класс RenewBookModelForm является функциональным эквивалентом нашему предыдущему классу RenewBookForm . Вы можете импортировать и использовать его в тех же местах, где и RenewBookForm .

    Обобщённые классы отображения для редактирования

    Алгоритм управления формой, который мы использовали в нашей функции отображения, является примером достаточно общего подхода к работе с формой. Django старается абстрагировать и упростить большую часть данной работы, путём широкого применения обобщённых классов отображений, которые служат для создания, редактирования и удаления отображений на основе моделей. Они не только управляют поведением отображения, но, кроме того, они из вашей модели автоматически создают класс формы ( ModelForm ).

    **Примечание:**В дополнение к отображениям для редактирования, описываемых здесь, существует также класс FormView, который по своему предназначению находится где-то между «простой» функцией отображения и другими обобщёнными отображениями, то есть в каком-то смысле, в диапазоне: «гибкость» против «усилия при программировании». Применяя FormView, вы все ещё нуждаетесь в создании класса Form , но вам не нужно реализовывать всю «стандартную» функциональность работы с формой. Вместо этого, вы должны просто реализовать функцию, которая будет вызвана в тот момент, когда станет понятно, что получаемые из формы данные, «правильные» (валидны).

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

    Отображения

    Откройте файл отображений (locallibrary/catalog/views.py) и добавьте следующий код в его нижнюю часть:

    from django.views.generic.edit import CreateView, UpdateView, DeleteView from django.urls import reverse_lazy from .models import Author class AuthorCreate(CreateView): model = Author fields = '__all__' initial='date_of_death':'12/10/2016',> class AuthorUpdate(UpdateView): model = Author fields = ['first_name','last_name','date_of_birth','date_of_death'] class AuthorDelete(DeleteView): model = Author success_url = reverse_lazy('authors') 

    Как вы видите, для создания отображений вам надо наследоваться от следующих классов CreateView , UpdateView и DeleteView (соответственно), а затем связать их с соответствующей моделью.

    Для случаев «создать» и «обновить» вам также понадобится определить поля для показа на форме (применяя тот же синтаксис, что и для ModelForm ). В этом случае мы демонстрируем синтаксис и для показа «всех» полей, и перечисление их по отдельности. Также вы можете указать начальные значения для каждого поля, применяя словарь пар имя_поля/значение (в целях демонстрации, в нашем примере мы явно указываем дату смерти — если хотите, то вы можете удалить это поле). По умолчанию отображения перенаправляют пользователя на страницу «успеха», показывая только что созданные/отредактированные данные (записи в модели). В нашем случае это, созданная в предыдущей части руководства, подробная информация об авторе. Вы можете указать альтернативное перенаправление при помощи параметра success_url (как в примере с классом AuthorDelete ).

    Классу AuthorDelete не нужно показывать каких либо полей, таким образом их не нужно и декларировать. Тем не менее, вам нужно указать success_url , потому что, в данном случае, для Django не очевидно что делать после успешного выполнения операции удаления записи. Мы используем функцию reverse_lazy() для перехода на страницу списка авторов после удаления одного из них — reverse_lazy() это более «ленивая» версия reverse().

    Шаблоны

    Отображения «создать» и «обновить» используют шаблоны с именем model_name_form.html, по умолчанию: (вы можете поменять суффикс на что-нибудь другое, при помощи поля template_name_suffix в вашем отображении, например, template_name_suffix = ‘_other_suffix’ )

    Создайте файл шаблона locallibrary/catalog/templates/catalog/author_form.html и скопируйте в него следующий текст.

     extends "base_generic.html" %>  block content %> form action="" method="post">  csrf_token %> table>  <form.as_table >> table> input type="submit" value="Submit" /> form>  endblock %> 

    Это напоминает наши предыдущие формы и рендер полей при помощи таблицы. Заметьте, что мы снова используем .

    Отображения «удалить» ожидает «найти» шаблон с именем формата model_name_confirm_delete.html (и снова, вы можете изменить суффикс при помощи поля отображения template_name_suffix ). Создайте файл шаблона locallibrary/catalog/templates/catalog/author_confirm_delete.html и скопируйте в него текст, указанный ниже.

     extends "base_generic.html" %>  block content %> h1>Delete Authorh1> p>Are you sure you want to delete the author:  <author >>?p> form action="" method="POST">  csrf_token %> input type="submit" value="Yes, delete." /> form>  endblock %> 

    Настройки URL-адресов

    Откройте файл конфигураций URL-адресов (locallibrary/catalog/urls.py) и добавьте в его нижнюю часть следующие настройки:

    += [ url(r'^author/create/$', views.AuthorCreate.as_view(), name='author_create'), url(r'^author/(?P\d+)/update/$', views.AuthorUpdate.as_view(), name='author_update'), url(r'^author/(?P\d+)/delete/$', views.AuthorDelete.as_view(), name='author_delete'), ] 

    Здесь нет ничего нового! Как вы видите отображения являются классами и следовательно должны вызываться через метод .as_view() . Паттерны URL-адресов для каждого случая должны быть вам понятны. Мы обязаны использовать pk как имя для «захваченного» значения первичного ключа, так как параметр именно с таким именем ожидается классами отображения.

    Страницы создания, обновления и удаления автора теперь готовы к тестированию (мы не будем создавать на них ссылки в отдельном меню, но вы, если хотите, можете их сделать).

    Примечание: Наблюдательные пользователи могли заметить, что мы ничего не делаем, чтобы предотвратить несанкционированный доступ к страницам! Мы оставили это в качестве упражнения для вас (подсказка: вы можете использовать PermissionRequiredMixin и, либо создать новое разрешение, или воспользоваться нашим прежним can_mark_returned ).

    Тестирование страницы

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

    Затем перейдите на страницу создания новой записи автора: http://127.0.0.1:8000/catalog/author/create/ , которая должна быть похожей на следующий скриншот.

    Form Example: Create Author

    Введите в поля значения и нажмите на кнопку Submit, чтобы сохранить новую запись об авторе. После этого, вы должны были перейти на страницу редактирования только что созданного автора, имеющий адрес, похожий на следующий http://127.0.0.1:8000/catalog/author/10 .

    У вас есть возможность редактирования записей при помощи добавления /update/ в конец адреса подробной информации (то есть, http://127.0.0.1:8000/catalog/author/10/update/ ) — мы не показываем скриншот, потому что он выглядит в точности также как страница «создать»!

    И последнее, мы можем удалить страницу, добавляя строку /delete/ в конец адреса подробной информации автора (то есть, http://127.0.0.1:8000/catalog/author/10/delete/ ). Django должен показать страницу, которая похожа на представленную ниже. Нажмите Yes, delete., чтобы удалить запись и перейти на страницу со списком авторов.

    Проверьте себя

    Создайте несколько форм создания, редактирования и удаления записей в модели Book . При желании, вы можете использовать тоже структуры как и в случае с моделью Authors . Если ваш шаблон book_form.html является просто копией шаблона author_form.html, тогда новая страница «create book» будет выглядеть как на следующем скриншоте:

    Итоги

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

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

    Смотрите также

    • Работа с формами (Django docs)
    • Создание вашего первого приложения, часть 4 > Создание простой формы (Django docs)
    • Forms API (Django docs)
    • Поля класса Form (Django docs)
    • Класс Form и валидация поля (Django docs)
    • Управление классом Form из классов отображений (Django docs)
    • Создание форм из моделей (Django docs)
    • Обобщённые отображения для редактирования (Django docs)
    • Назад (en-US)
    • Обзор: Django
    • Далее

    Found a content problem with this page?

    • Edit the page on GitHub.
    • Report the content issue.
    • View the source on GitHub.

    This page was last modified on 3 авг. 2023 г. by MDN contributors.

    Создание форм с Django

    Начнем с создания формы для отправки постов по email. Джанго имеет встроенные механизмы(forms framework), позволяющие легко создавать формы. Forms framework позволяет определить поля формы, указать способ их отображения и указать, как они должны проверять входные данные.

    Django имеет два базовых класса для построения форм:

    • Form: позволяет создавать стандартные формы
    • ModelForm: позволяет создавать формы для создания или обновления экземпляров модели

    Для начала создайте файл forms.py внутри вашего приложения ‘blog’, и отредактируйте его следующим образом:

    from django import forms class EmailPostForm(forms.Form): name = forms.CharField(max_length=25) email = forms.EmailField() to = forms.EmailField() comments = forms.CharField(required=False, widget=forms.Textarea) 

    Это твоя первая форма Джанго. Рассмотрим код: мы создали форму путем наследования базового класса Form. Мы используем различные типы полей Джанго для их валидации.

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

    Поле name является текстовым полем CharField. Этот тип поля отображается в HTML как . Каждый тип поля имеет графический элемент по умолчанию, который определяет, как это поле отображается в HTML. Графический элемент по умолчанию может быть переопределен атрибутом widget. В поле comments используется графический элемент textarea для отображения его в виде HTML-элемента , а не

    Валидация полей также зависит от типа поля. Например, поля email и to являются EmailField. Для обоих полей требуется допустимый адрес электронной почты, в противном случае валидация получит ValidationError исключение и форма не будет отправляться. Другие параметры также принимаются во внимание при проверке формы: мы определим максимальную длину 25 символов для поля name и сделали поле comments необязательным с required = false. Все это также учитывается при валидации. Типы полей, используемые в этой форме, являются частью полей «Джанго». Список всех доступных полей формы можно посмотреть здесь: https://docs.djangoproject.com/en/1.8/ref/forms/fields/

    results matching » «

    No results matching » «

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *