Блог на Django #24: Создание и обработка форм из моделей

Все еще нужна форма, чтобы пользователи могли оставлять комментарии к записям. В Django есть два базовых класса для построения форм: Form и ModelForm. Первый уже использовался для того, чтобы пользователи могли делиться постами через электронную почту. Сейчас нужно использовать ModelForm, потому что форму необходимо создавать динамически на основе Comment. Отредактируйте файл forms.py приложения blog и добавьте следующие строки:

from .models import Comment

class CommentForm(forms.ModelForm):  
    class Meta:  
        model = Comment  
        fields = ('name', 'email', 'body')

Для создания формы на основе модели нужно просто указать, какую модель взять за основу в классе формы Meta. Django исследует модель и строит форму динамически. Каждое поле модели имеет соответствующий тип поля формы по умолчанию. Способ, которым были определены поля модели, учитывается при проверке формы. По умолчанию Django создает поле формы для каждого поля модели. Но можно явно указать фреймворку, какие поля нужны в форме с помощью списка fields или определения того, какие поля нужно исключить с помощью списка полей exclude. Для формы CommentForm будут использоваться name, email и body, потому что это единственное, что нужно заполнять.

Обработка ModelForms в представлениях

Для простоты представление поста будет использоваться для создания экземпляра формы и ее обработки. Отредактируйте файл views.py, добавьте импорты для модели Comment и формы CommentForm и отредактируйте представление post_detail, чтобы оно выглядело вот так:

from .models import Post, Comment 
from .forms import EmailPostForm, CommentForm

def post_detail(request, year, month, day, post):  
    post = get_object_or_404(Post, slug=post,  
				   status='published',  
				   publish__year=year,  
				   publish__month=month,  
				   publish__day=day)  
      
    # Список активных комментариев к этой записи  
    comments = post.comments.filter(active=True)  
    new_comment = None  
    if request.method == 'POST':  
        # Комментарий был опубликован
	comment_form = CommentForm(data=request.POST)  
        if comment_form.is_valid():  
            # Создайте объект Comment, но пока не сохраняйте в базу данных
	    new_comment = comment_form.save(commit=False)  
            # Назначить текущий пост комментарию
	    new_comment.post = post  
            # Сохранить комментарий в базе данных 
	    new_comment.save()  
    else:  
        comment_form = CommentForm()  
    return render(request,  
		  'blog/post/detail.html',  
		  {'post': post,  
		  'comments': comments,  
		  'new_comment': new_comment,  
		  'comment_form': comment_form})

Разберем, что есть в представлении. Представление post_detail используется для отображения поста и комментариев. С помощью QuerySet можно получить все активные комментарии:

comments = post.comments.filter(active=True)

Начинается этот QuerySet с объекта post. Менеджер для связанных объектов, определенный в comments, используется при помощи атрибута related_name отношения в модели Comment.

То же представление используется для того, чтобы пользователи могли оставлять комментарии. Переменная new_comment инициализируется при передаче ей значения None. Она создается при создании комментария. Экземпляр формы создается с помощью comment_form = CommentForm(), если представление вызывается посредством запроса GET. Если он делается через POST, форма экземпляра создается с помощью отправленных данных и проверяется через метод is_valid(). Если форма неверна, рендерится шаблон с ошибками проверки. Если правильная — выполняются следующие действия:

  1. Создается новый объект Comment посредством вызова метода формы save() и присваивания его переменной new_comment следующим образом:
    new_comment = comment_form.save(commit=False)
    

    Метод save() создает экземпляр модели, к которой привязана форма и сохраняет ее в базу данных. Если ее вызвать с помощью commit=False, то экземпляр будет создан, но сохранение в базу данных не состоится. Это удобно, когда нужно изменить объект перед сохранением. А это следующий этап.

    Метод save() доступен для ModelForm, но не для экземпляров Form, потому что они не привязаны ни к одной модели.

  2. Текущий пост присваивается созданному комментарию:
    new_comment.post = post
    

    Таким образом отмечается, что этот комментарий принадлежит этому посту.

  3. Наконец, новый комментарий сохраняется в базу данных через метод save():
    new_comment.save()
    

Представление готово отображать и обрабатывать новые комментарии.