Select it with the mouse and hit Enter
| на поддержку перевода |
|
|
ЯМ:41001223475816
Практически во всех источниках по 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, не пожалеете.
Надеюсь, обработка форм теперь для вас не проблема ;)
> exception ловятся в мидлваре.
И в чем смысл этого всего? Если методу нужен user, так зачем делать по дефолту None? Если форма должна работать как с user так и без - тогда обрабатывать оба случая в методе.
> if user is None:
Забыли проверить is_authenticated()
reply to rad
Приведите, пожалуйста, ваш вариант кода, если не сложно. Интересует случай с обработкой случая, когда пользователь не передан в save().
from django.contrib.auth.models import User
from django.db import models
class MyException(Exception):
pass
class MyModel(models.Model):
author = models.ForeignKey(User)
def save(self, user = None, *args, **kwargs):
if user is None:
raise MyException
self.author = user
return super(MyModel, self).save(*args, **kwargs)
exception ловятся в мидлваре.
Статья отредактирована в соответствии с рекомендациями catavaran@LJ.
reply to Belegnar
user правильней передавать в form.save() и дополнительно обрабатывать случай, когда юзер не передается.
Приведите, пожалуйста, ваш вариант кода, если не сложно. Интересует случай с обработкой случая, когда пользователь не передан в save().
user правильней передавать в form.save() и дополнительно обрабатывать случай, когда юзер не передается.