Обработка форм с использованием представлений на основе классов
Обработка форм обычно имеет 3 этапа:
- Изначальный GET-запрос (пустая или заполненная форма)
- POST-запрос с некорректными данными (как правило, повторный вывод формы с ошибками)
- POST-запрос с корректными данными (обработка данных и, как правило, переадресация)
Реализация этого самостоятельно часто приводит к большому количеству повторяющегося кода (см. Использование формы в представлении). Для избежания этого Django предоставляет набор универсальных представлений на основе классов для обработки форм.
Базовые формы
Рассмотрим форму обратной связи:
forms.pyfrom django import forms
class ContactForm(forms.Form):
name = forms.CharField()
message = forms.CharField(widget=forms.Textarea)
def send_email(self):
# send email using the self.cleaned_data dictionary
pass
Представление можно создать, используя FormView
views.pyfrom myapp.forms import ContactForm
from django.views.generic.edit import FormView
class ContactFormView(FormView):
template_name = "contact.html"
form_class = ContactForm
success_url = "/thanks/"
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
form.send_email()
return super().form_valid(form)
Примечания:
- FormView наследует
TemplateResponseMixin, поэтому можно использоватьtemplate_name. - По умолчанию реализация для
form_valid()просто перенаправляет наsuccess_url.
Формы моделей
Универсальные представления действительно хорошо работают с моделями. Эти универсальные представления автоматически создадут ModelForm, если смогут определить, какую модель использовать:
- Если задан атрибут
model, будет использоваться этот класс модели. - Если
get_object()возвращает объект, будет использоваться класс этого объекта. - Если задан
queryset, будет использоваться модель для этого набора данных.
Представления форм моделей предоставляют реализацию form_valid(), которая автоматически сохраняет модель. Вы можете переопределить её, если у вас есть особые требования; примеры ниже.
Вам даже не нужно предоставлять success_url для CreateView или UpdateView – они будут использовать get_absolute_url() на объекте модели, если он доступен.
Если вы хотите использовать пользовательскую ModelForm (например, для добавления дополнительной валидации), установите form_class в вашем представлении.
Примечание
При указании пользовательского класса формы, вы всё равно должны указать модель, даже если form_class является ModelForm.
Сначала нам нужно добавить get_absolute_url() к нашему Author классу:
models.pyfrom django.db import models
from django.urls import reverse
class Author(models.Model):
name = models.CharField(max_length=200)
def get_absolute_url(self):
return reverse("author-detail", kwargs={"pk": self.pk})
Затем мы можем использовать CreateView и аналогичные представления для выполнения реальной работы. Обратите внимание, как мы здесь только настраиваем универсальные представления на основе классов; нам не нужно писать никакой логики самостоятельно:
views.pyfrom django.urls import reverse_lazy
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from myapp.models import Author
class AuthorCreateView(CreateView):
model = Author
fields = ["name"]
class AuthorUpdateView(UpdateView):
model = Author
fields = ["name"]
class AuthorDeleteView(DeleteView):
model = Author
success_url = reverse_lazy("author-list")
Примечание
Мы должны использовать reverse_lazy(), а не reverse(), поскольку URL не загружаются при импорте файла.
Атрибут fields работает так же, как атрибут fields во внутреннем Meta классе ModelForm. Если вы не определяете класс формы другим способом, этот атрибут необходим, и представление выдаст исключение ImproperlyConfigured, если он не указан.
Если вы укажете как атрибут fields, так и form_class, будет выброшено исключение ImproperlyConfigured.
Наконец, мы подключаем эти новые представления к URLconf:
urls.pyfrom django.urls import path
from myapp.views import AuthorCreateView, AuthorDeleteView, AuthorUpdateView
urlpatterns = [
# ...
path("author/add/", AuthorCreateView.as_view(), name="author-add"),
path("author/<int:pk>/", AuthorUpdateView.as_view(), name="author-update"),
path("author/<int:pk>/delete/", AuthorDeleteView.as_view(), name="author-delete"),
]
Примечание
Эти представления наследуют SingleObjectTemplateResponseMixin, которое использует template_name_suffix для построения template_name на основе модели.
В этом примере:
-
CreateViewиUpdateViewиспользуютmyapp/author_form.html -
DeleteViewиспользуетmyapp/author_confirm_delete.html
Если вы хотите иметь отдельные шаблоны для CreateView и UpdateView, вы можете установить либо template_name, либо template_name_suffix в вашем классе представления.
Модели и request.user
Для отслеживания пользователя, создавшего объект с помощью CreateView, вы можете использовать пользовательскую ModelForm для этого. Сначала добавьте внешнюю связь с моделью:
models.pyfrom django.contrib.auth.models import User
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=200)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
# ...
В представлении убедитесь, что вы не включаете created_by в список полей для редактирования, и переопределите form_valid(), чтобы добавить пользователя:
views.pyfrom django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreateView(LoginRequiredMixin, CreateView):
model = Author
fields = ["name"]
def form_valid(self, form):
form.instance.created_by = self.request.user
return super().form_valid(form)
LoginRequiredMixin предотвращает доступ к форме пользователей, которые не авторизованы. Если вы этого не сделаете, вам нужно будет обрабатывать неавторизованных пользователей в form_valid().
Пример переговорного процесса для содержимого
Вот пример того, как вы можете реализовать форму, которая работает как с потоком API, так и с обычными POST-запросами форм:
from django.http import JsonResponse
from django.views.generic.edit import CreateView
from myapp.models import Author
class JsonableResponseMixin:
"""
Mixin to add JSON support to a form.
Must be used with an object-based FormView (e.g. CreateView)
"""
def form_invalid(self, form):
response = super().form_invalid(form)
if self.request.accepts("text/html"):
return response
else:
return JsonResponse(form.errors, status=400)
def form_valid(self, form):
# We make sure to call the parent's form_valid() method because
# it might do some processing (in the case of CreateView, it will
# call form.save() for example).
response = super().form_valid(form)
if self.request.accepts("text/html"):
return response
else:
data = {
"pk": self.object.pk,
}
return JsonResponse(data)
class AuthorCreateView(JsonableResponseMixin, CreateView):
model = Author
fields = ["name"]
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/4.2/topics/class-based-views/generic-editing/