Курс по Django: https://stepik.org/a/183363
Архив проекта: 47_sitewomen.zip
На предыдущем
занятии мы с вами познакомились с созданием форм, не связанных с моделями на
примере добавления статьи. Это был несколько искусственный пример, так как
добавление нового поста связано с обращением к БД. Из-за этого у нас,
фактически, получилось дублирование кода: в классе формы AddPostForm мы
прописывали аналогичные атрибуты, что и в классе модели Women. Это не очень
хорошо. И когда форма предполагает тесное взаимодействие с какой-либо моделью,
то лучше ее напрямую с ней и связать. На этом занятии вы увидите, как это
делается.
Перейдем в файл women/forms.py и класс AddPostForm унаследуем от
другого базового класса ModelForm. А внутри дочернего класса
объявим вложенный класс Meta с атрибутами model и fields:
class AddPostForm(forms.ModelForm):
class Meta:
model = Women
fields = '__all__'
Атрибут model как раз
устанавливает связь формы с моделью Women, а свойство fields – определяет
поля для отображения в форме. Значение __all__ указывает показывать
все поля, кроме тех, что заполняются автоматически. В результате, мы увидим уже
готовую форму, только без полей time_create и time_update, так как они
наполняются без участия пользователя.
Однако на
практике рекомендуется явно прописывать список полей, необходимых для
отображения в форме. В нашем случае это будет выглядеть так:
fields = ['title', 'slug', 'content', 'is_published', 'cat', 'husband', 'tags']
Затем, чтобы
описать стили оформления для каждого поля, используется атрибут widgets класса Meta:
class Meta:
model = Women
fields = ['title', 'slug', 'content', 'is_published', 'cat', 'husband', 'tags']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-input'}),
'content': forms.Textarea(attrs={'cols': 60, 'rows': 10}),
}
Обновляем
страницу и видим, что эти свойства были применены к указанным полям.
По идее нам бы
еще хотелось у списков установить свойства empty_label. Для этого явно пропишем
атрибуты для полей выбора, как это мы делали в предыдущей модели:
class AddPostForm(forms.ModelForm):
cat = forms.ModelChoiceField(queryset=Category.objects.all(), empty_label="Категория не выбрана", label="Категории")
husband = forms.ModelChoiceField(queryset=Husbands.objects.all(), required=False, empty_label="Не замужем", label="Муж")
class Meta:
model = Women
fields = ['title', 'slug', 'content', 'is_published', 'cat', 'husband', 'tags']
labels = {'slug': 'URL'}
widgets = {
'title': forms.TextInput(attrs={'class': 'form-input'}),
'content': forms.Textarea(attrs={'cols': 50, 'rows': 5}),
}
Теперь, наша
форма ничем не отличается от предыдущего варианта. Мало того, в ней появился
метод save(), который
сохраняет переданные данные в БД. Мы им и воспользуемся. Перейдем в файл women/views.py и в функции
представления addpage() вместо
прежней конструкции:
Women.objects.create(**form.cleaned_data)
запишем:
Все, форма
готова к использованию! Видите, как фреймворк Django позволяет
предельно автоматизировать весь процесс создания и обработки данных форм.
Давайте убедимся, что все работает. Перейдем на страницу:
http://127.0.0.1:8000/addpage/
Я специально
укажу неуникальный URL и, смотрите, для формы связанной с моделью мы
получаем четкое встроенное сообщение о проблеме. То есть, метод save() берет на себя
всю проверку корректности записи данных и блок try except нам уже не
нужен. Уберем его. Введем уникальный URL и пост успешно добавляется в БД.
Этот пример
показывает, насколько упрощается взаимодействие между пользователем и БД, с
использованием форм, связанных с моделью.
Создание собственных валидаторов формы
Если стандартных
проверок для валидации полей формы недостаточно, то мы по-прежнему можем формировать
свои собственные валидаторы, также как и у форм, не связанных с моделью.
Например, опишем валидатор для поля title, который бы не
позволял вводить строку более 50 символов. Как я уже отметил в SQLite эта проверка не
проходит, а просто обрезается заголовок до указанной длины, а мы сделаем так,
что пользователю будет показываться сообщение, что название статьи слишком
большое.
Для этого в форме
AddPostForm объявим метод с
именем clean_title и следующим
содержимым:
def clean_title(self):
title = self.cleaned_data['title']
if len(title) > 50:
raise ValidationError('Длина превышает 50 символов')
return title
Мы здесь вначале
считываем заголовок, переданный из формы, из словаря очищенных данных – cleaned_data. Затем,
проверяем, если длина больше 50 символов, то пользователю будет показываться
сообщение «Длина превышает 50 символов». Иначе, возвращается заголовок title, который и
будет помещен в БД.
Как видите, все
предельно просто. Проверим, как это работает. Введем заголовок длиной более 50
символов, заполним другие поля и при нажатии на кнопку «Добавить» увидим
искомое сообщение. Все, мы сделали свой собственный валидатор для поля title на уровне формы.
По аналогии можно создавать другие валидаторы для остальных полей, если в этом
возникает необходимость.
Курс по Django: https://stepik.org/a/183363