Django Framework / Немного подробностей про Class Based Views, ч.4

в 21:39, , рубрики: cbv, class based views, django, python, метки: , , ,

Здравствуйте! В продолжении серии статей про Class Based Views (далее CBV) переходим к разделу, посвященному редактированию объектов. В данной статье мы рассмотрим четыре класса с говорящими названиями: FormView, CreateView, UpdateView, DeleteView.Часть 1, часть 2, часть 3, часть 4
Создание и обработка формы с помощью CBV

Для ряда действий, будь то регистрация или авторизация на сайте, публикация новости, комментария или добавление товара в магазине, невозможно обойтись без форм. В качестве универсального инструмента создания форм в Django выступает класс FormView. В самом просто случае для создания работоспособной формы достаточно лишь передать ему лишь ссылку на класс, описывающий необходимую форму:
from django.views.generic.edit import FormView

class RegisterForm(FormView):
form_class = Register
success_url = '/thanks/'

или передать нужные данные непосредственно экземпляру класса FormView в нашем urlconf:
url(r'^register/$', FormView.as_view(form_class=Register, success_url='/thanks/')

Note: Пример синтетический и, разумеется, в таком виде использовать для страницы регистрации не получится.
Класс формы, который необходимо обработать, возвращается методом get_form_class. По умолчанию данный метод возвращает атрибут form_class, которому мы присваивали класс нашей формы в примере выше. Мы можем переопределить данный метод, если нам требуется более сложная логика в определении класса формы.
Метод get_success_url возвращает url ссылку, на которую будет осуществляться переход после успешной обработки формы. По умолчанию данный метод возвращает атрибут success_url.
Для указания полям формы значений по умолчанию мы можем передать их непосредственно в атрибут initial, представляющий из себя словарь, ключи которого должны иметь имена требуемых полей формы. Значение данного атрибута возвращается по умолчанию методом get_initial.
Часто возникает необходимость передать в форму определенные данные, например объект пользователя или заранее определенный список разделов. Для данного действия подходит метод get_form_kwargs. При переопределении данного метода необходимо соблюдать осторожность и не переписать случайно данные, передаваемые в форму по умолчанию. Среди них:
def get_form_kwargs(self):
"""
Возвращает словарь аргументов для экземпляра формы
"""
kwargs = {'initial': self.get_initial()}
if self.request.method in ('POST', 'PUT'):
kwargs.update({
'data': self.request.POST,
'files': self.request.FILES,
})
return kwargs

Чтобы избежать потери этих данных мы должны сначала получить словарь из родительского класса, затем добавить в него требуемые данные:
class ProductForm(FormView):

def get_form_kwargs(self):
kwargs = super(ProductForm, self).get_form_kwargs()
kwargs.update({
'sections': Section.objects.filter(is_active=True)
})
return kwargs

Для получения класса нашей формы мы можем использовать метод get_form, который по умолчанию возвращает класс формы, указанный через метод form_class, с переданным ему словарем метода get_form_kwargs:
def get_form(self, form_class):
return form_class(**self.get_form_kwargs())

При обработке формы, в случае успеха, Django вызывает метод form_valid нашего отображения. По умолчанию данный метод осуществляет редирект по ссылке, возвращаемой методом get_success_url.
В случае, когда форме переданы некорректные данные вызывается метод form_invalid, который по умолчанию возвращает пользователя обратно на страницу формы, передавая ей объект со списком ошибок валидации.
class CreatePost(FormView):
form_class = PostForm
template_name = 'create_post.html'
success_url = '/success/'

@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
super(CreatePost, self).dispatch(request, *args, **kwargs)

def form_valid(self, form):
Post.objects.create(**form.cleaned_data)
return redirect(self.get_success_url())

Теперь рассмотрим разные сферы применения форм более подробно.
Создание нового экземпляра объекта

С помощью примера выше мы можем с легкостью создать новый объект статьи, однако в Django есть средства, позволяющие создать объект с еще большей легкостью, это класс CreateView. Для применения данного класса, передаваемая ему форма должна наследоваться от ModelForm:
from django import forms

class NewsForm(forms.ModelForm):
class Meta(object):
model = News
exclude = ('status',)

Теперь мы можем передать объект данной формы в наше отображение. Пример отображения почти аналогичен предыдущему:
from django.views.generic.edit import CreateView

class CreateNews(CreateView):
form_class = NewsForm
template_name = 'create_news.html'
succes_url = '/success/'

def form_valid(self, form):
Post.objects.create(**form.cleaned_data)
return redirect(self.get_success_url())

Note: Определение метода form_valid в данном примере излишне, так как CreateView наследует ModelFormMixin, который сохраняет экземпляр объекта из формы автоматически.
Для обработки ошибок также используется метод form_invalid, функциональность которого аналогична оной класса FormView.
Обновление экземпляра объекта

Основное отличие класса UpdateView от CreateView, это передача экземпляра изменяемого объекта атрибуту object данного класса, в остальном данные классы идентичны. Для редактирования нам достаточно передать в url первичный ключ или slug изменяемого объекта:
# Маршрут в urlconf
url(r'^item/(?Pd+)/edit/$', ItemUpdate.as_view()),

# Отображение в views.py

from django.views.generic.edit import UpdateView

class ItemUpdate(UpdateView):
form_class = ItemForm
model = Item
template_name = 'create_item.html'
success_url = '/success/'

# Форма

form django import forms

class NewsForm(forms.ModelForm):
class Meta(object):
model = Item
exclude = ('status',)

# Модель

from django.db import models

class Item(models.Model):
name = models.CharField(max_length=32, verbose_name=u'Название')
description = models.TextField(verbose_name=u'Описание')
status = models.BooleanField(default=True)

def __unicode__(self):
return self.name

Описание формы и модели для класса CreateView выглядит аналогичным образом.
Удаление экземпляра объекта

Логика отображения для удаления объекта несколько отличается от рассмотренных предыдущих классов в данной статье. Удаление объекта, в целях безопасности, доступно лишь после передачи post или delete запроса. В случае get запроса будет отображена страница с подтверждением удаления объекта. В самом минималистичном варианте она может представлять из себя форму с кнопкой отправки и csrf токеном:

{% csrf_token %}

Отображение может выглядеть следующим образом:
from django.views.generic.edit import DeleteView

class ItemDelete(DeleteView):
model = Item
template_name = 'item_confirm_delete.html'
success_url = '/success/'

Разумеется если вам потребуются соответсвующие проверки на наличие авторизации у пользователей или соответствующих прав, то вы можете сделать это используя декораторы для метода dispatch или добавить свою логику проверки:
from django.views.generic.edit import DeleteView

class ItemDelete(DeleteView):
model = Item
template_name = 'item_confirm_delete.html'
success_url = '/success/'

@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
super(ItemDelete, self).dispatch(request, *args, **kwargs)

В данной статье мы рассмотрели работу с формами и управлением объектами, если у вас возникнут какие-либо вопросы, то я или другие читатели, надеюсь, смогут на них ответить. Также прошу сообщить обо всех найденных ошибках или неточностях и предложения по добавлению информации в статью, если я что-то пропустил. Спасибо, что прочитали статью и желаю счастливых выходных =)

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js