Практически во всех источниках по Django показаны неэффективные способы обработки форм.
Покажу выверенный годами подход.
Во-первых, освойте наконец функцию direct_to_template! В связи с этим обработка формы значительно упрощается.
Обычная форма входа на сайт
Шаблон для отображения формы:
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans 'Authentication' %}{% endblock %}
{% block content %}
{% if user.is_anonymous %}
<form method="post">
{% csrf_token %}
<table>
{{ form }}
<tr>
<td colspan="2">
<input type="submit" name="login" value="{% trans 'Login' %}"/>
</td>
</tr>
</table>
</form>
{% endif %}
{% endblock %}
Обратите внимание на тэг {% csrf_token %}
. Он обязателен для работы форм!
Простейшая форма для ввода логина и пароля:
from django import forms
from django.utils.translation import ugettext as _
class UserLoginForm(forms.Form):
username = forms.CharField(label=_(u'Username'), max_length=30)
password = forms.CharField(label=_(u'Password'), widget=forms.PasswordInput)
Теперь самое интересное - представление:
from django.contrib import auth
from django.shortcuts import redirect
from django.views.generic.simple import direct_to_template
def user_login_view(request):
form = UserLoginForm(request.POST or None)
context = { 'form': form, }
if request.method == 'POST' and form.is_valid():
username = form.cleaned_data.get('username', None)
password = form.cleaned_data.get('password', None)
user = auth.authenticate(username=username, password=password)
if user and user.is_active:
auth.login(request, user)
return redirect('index_page')
return direct_to_template(request, 'form.html', context)
Сначала инициируется объект формы, если это был GET вызов, то класс
получит None
, иначе информацию, отправленную пользователем. Затем
помещаем ссылку на объект в контекст шаблона.
Если это был GET вызов, то просто отображаем форму для заполнения.
Если это был POST, то проверим переданные данные с помощью метода
is_valid()
, при этом вызываются валидаторы формы. Если проверка
пройдена успешно, то получаем значения каждого поля. Используя эти
значения, производим аутентификацию пользователя. Если это нам удалось
и пользователю разрешена работа с системой выполняем для него вход в
систему и выполняем перенаправление на главную страницу сайта.
Обратите внимание на то, что при любой ошибке происходит отображение заполенной формы, пользователь сможет уточнить данные и повторно их отправить.
Обычная форма из модели
Рассмотрим более продвинутый способ использования форм.
Есть модель, описывающая некий предмет:
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
class ItemDescModel(models.Model):
""" Item Description Model. """
customer = models.ForeignKey(User, verbose_name=_(u'Customer'))
url = models.URLField(verbose_name=_('Item\'s URL'), verify_exists=(settings.DEBUG==False))
title = models.CharField(verbose_name=_(u'Item\'s Title'), max_length=128)
reg_datetime = models.DateTimeField(verbose_name=_(u'Registered'), auto_now_add=True)
class Meta:
verbose_name = _(u'Item Description')
verbose_name_plural = _(u'Item Descriptions')
ordering = ('-reg_datetime',)
def __unicode__(self):
return self.title
Необходимо организовать работу с её данными на сайте.
Django предоставляет возможность создать форму из модели:
from django import forms
from django.utils.translation import ugettext as _
class ItemDescForm(forms.ModelForm):
class Meta:
model = models.ItemDescModel
fields = ('title', 'url',)
def save(self, user):
obj = super(ItemDescForm, self).save(commit=False)
obj.customer = user
return obj.save()
Обратите внимание, что на форме выводится только два поля: title
и
url
. Поле reg_datetime
заполняется автоматически, а поле customer
мы
заполняем в методе save()
. Для чего в метод передаётся информация о
пользователе. Метод должен возвращать созданный объект.
Теперь рассмотрим обработку такой формы:
from django.shortcuts import redirect
from django.shortcuts import get_object_or_404
from django.contrib.auth.decorators import login_required
from django.views.generic.simple import direct_to_template
@login_required
def item_desc_view(request, item_id):
item = get_object_or_404(models.ItemDescModel, id=item_id)
form = forms.ItemDescForm(request.POST or None, instance=item)
context = { 'item': item, 'form': form, }
if request.method == 'POST' and form.is_valid():
form.save(request.user)
return redirect('item_desc', item_id=item.id)
return direct_to_template(request, 'form.html', context)
Обратите внимание на ещё один декоратор - login_required
. Данный
декоратор обеспечивает доступ к обёрнутому представлению только для
зарегистрированных пользователей.
Сначала по переданному идентификатору предмета находим его в
модели. Форму инициализируем как обычно, но чтобы заполнить её данными
о предметы следует передать параметр instance
.
Дальше всё как в предыдущем примере, только немного усложнился способ получения URL для перенаправления - там передаётся дополнительный параметр, идентификатор предмета.
Чтобы было понятно как он используется привожу urls.py
:
urlpatterns = patterns(
'views',
url(r'^(?P<item_id>[0-9a-f]{32})/$', 'item_desc_view', name='item_desc'),
)
Последняя строчка обеспечивает обработку URL вида:
http://domain.ru/3deb1f0ad5867fc69ab4c7f77a0453de/
Теперь добавим отображение простой страницы, на которую происходит переход при успешном заполнении формы:
urlpatterns += patterns('django.views.generic.simple',
url(r'^$', 'direct_to_template', {'template': 'main.html'}), name='index_page'),
Не забывайте, что все эти формы, модели - это объекты, у них есть конструкторы, методы и свойства. С ними можно творить всё, что угодно, меняя их поведение в соответствии с ситуацией. Изучите ООП языка Python, не пожалеете.
Надеюсь, обработка форм теперь для вас не проблема ;)