Found misprint?

Select it with the mouse and hit Enter

Ctrl-Enter
Processed:
56 1 199 25
на поддержку перевода
Яндекс Яндекс.Деньги Хочу такую же кнопку
Ускорить процесс перевода!

ЯМ:41001223475816

Редактор ресурсов Gettext для Android
The full repository of DjangoBook translation you can get on GitHub.
We appreciate your patches!
We are glad to hear your questions, comments or suggestions!
(Open in new tab)
Users number: 762
Русская группа
на Google

Основные методы обработки форм

Формы

Практически во всех источниках по 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, не пожалеете.

Надеюсь, обработка форм теперь для вас не проблема ;)

alerion
alerion 1 year, 1 month ago
Answer | Link

> exception ловятся в мидлваре.

И в чем смысл этого всего? Если методу нужен user, так зачем делать по дефолту None? Если форма должна работать как с user так и без - тогда обрабатывать оба случая в методе.

> if user is None:

Забыли проверить is_authenticated()

Belegnar
Belegnar 1 year, 1 month ago
Answer | Link

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 ловятся в мидлваре.

rad
rad 1 year, 2 months ago
Answer | Link

Статья отредактирована в соответствии с рекомендациями catavaran@LJ.

rad
rad 1 year, 2 months ago
Answer | Link

reply to Belegnar
user правильней передавать в form.save() и дополнительно обрабатывать случай, когда юзер не передается.

Приведите, пожалуйста, ваш вариант кода, если не сложно. Интересует случай с обработкой случая, когда пользователь не передан в save().

Belegnar
Belegnar 1 year, 2 months ago
Answer | Link

user правильней передавать в form.save() и дополнительно обрабатывать случай, когда юзер не передается.