Тема 6 из 6

Формы и блог с комментариями

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

Form

from django import forms


class CommentForm(forms.Form):
    name = forms.CharField(max_length=50)
    email = forms.EmailField(required=False)
    text = forms.CharField(min_length=3, max_length=500, widget=forms.Textarea)

Поле формы знает, как проверить значение: CharField (длина, по умолчанию сам убирает пробелы по краям), EmailField, IntegerField(min_value=, max_value=), ChoiceField(choices=…), BooleanField… Все поля обязательны, пока не указано required=False.

form = CommentForm(request.POST)
if form.is_valid():
    form.cleaned_data["name"]      # очищенное значение нужного типа
else:
    form.errors                    # {"text": ["Убедитесь, что…"], …}

Форма умеет и рисовать себя: {{ form.as_p }} в шаблоне выведет поля с подписями и ошибками.

Свои проверки

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ["title", "text"]

    def clean_title(self):
        title = self.cleaned_data["title"]
        if Post.objects.filter(title__iexact=title).exists():
            raise forms.ValidationError("Такой пост уже есть")
        return title
  • clean_<поле> вызывается после встроенных проверок поля; ValidationError превращается в ошибку этого поля;
  • метод обязан вернуть значение — оно попадёт в cleaned_data. Забытый return — частая ошибка: значение станет None;
  • clean(self) без имени поля проверяет поля вместе (например, «пароль и повтор совпадают»).

ModelForm

ModelForm строит поля по модели — не нужно описывать их второй раз. После проверки form.save() создаёт запись (или обновляет, если форма создана с instance=пост).

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

def add_comment(request, pk):
    post = get_object_or_404(Post, pk=pk)
    form = CommentForm(request.POST)
    if not form.is_valid():
        return HttpResponse(" ".join(form.errors), status=400)
    Comment.objects.create(post=post, **form.cleaned_data)
    return redirect("post-detail", pk=post.pk)

После успешной отправки — перенаправление (как во Flask: Post/Redirect/Get). redirect принимает имя маршрута и его аргументы.

В настоящем проекте в настройках включён CsrfViewMiddleware, и в каждую форму добавляют {% csrf_token %} — защиту от отправки формы с чужого сайта.

Что дальше

Вы прошли основы: адреса, шаблоны, модели, связи и формы. Дальше — настоящий проект (django-admin startproject blog), админ-панель django.contrib.admin (её можно получить почти даром), пользователи django.contrib.auth и Django REST Framework для API.

Итог

  • forms.Form, поля с ограничениями, required=False;
  • is_valid(), cleaned_data, errors;
  • clean_<поле> + ValidationError + обязательный return;
  • ModelForm и form.save(); после POST — redirect.