ModelForm и Form
ModelForm
- это обычная Form
, которая может автоматически генерировать определенные поля. Поля, которые генерируются автоматически, зависят от содержимого Meta
класса и от того, какие поля уже были определены декларативно - прописаны в ручную в классе формы. Фактически, ModelForm
будет только генерировать поля, которые отсутствуют в форме, или другими словами, поля, которые не были определены декларативно.
Поля, определенные декларативно, остаются неизменными, поэтому любые настройки, сделанные в атрибутах Meta
, таких как widgets
, labels
, help_texts
или error_messages
, игнорируются; они применяются только к полям, которые генерируются автоматически.
Аналогично, поля, прописанные в ручную, не используют свои атрибуты, такие как max_length
или required
, из соответствующей модели. Если вы хотите сохранить поведение, указанное в модели, вы должны явно установить соответствующие аргументы при объявлении поля формы.
Например, если модель Article
выглядит так:
class Article(models.Model):
headline = models.CharField(
max_length=200,
null=True,
blank=True,
help_text="Используйте каламбуры щедро",
)
content = models.TextField()
и вы хотите выполнить некоторую пользовательскую валидацию для headline
, сохраняя значения blank
и help_text
, как указано, вы можете определить ArticleForm
следующим образом:
class ArticleForm(ModelForm):
headline = MyFormField(
max_length=200,
required=False,
help_text="Используйте каламбуры щедро",
)
class Meta:
model = Article
fields = ["headline", "content"]
class Meta и основные атрибуты
Все основные атрибуты, отвечающие за поведение формы, связанной с моделью, задаются в классе Meta этой формы.
-
model: Атрибут
model
указывает, какая модель Django должна быть связана с этой формой. Это обязательный атрибут, так как он определяет, какие поля будут автоматически сгенерированы для формы.Например:
ArticleForm
будет связана с модельюArticle
, и все поля этой модели будут автоматически включены в форму.class ArticleForm(ModelForm): class Meta: model = Article
-
fields: Атрибут
fields
определяет список полей модели, которые должны быть включены в форму. Для формы обязательно должны быть заданыfields
илиexclude
. Чтобы отобразить все поля, можно использовать следующий подход (хоть он и не рекомендуется)fields = '__all__'
.Например: форма
ArticleForm
будет включать только поляtitle
иcontent
из моделиArticle
.class ArticleForm(ModelForm): class Meta: model = Article fields = ['title', 'content']
-
exclude: Атрибут
exclude
определяет список полей модели, которые должны быть исключены из формы. Он игнорируется, если одновременно задан атрибутfields
.Например: форма
ArticleForm
будет включать все поля моделиArticle
, кроме поляslug
.class ArticleForm(ModelForm): class Meta: model = Article exclude = ['slug']
-
widgets: Атрибут
widgets
определяет виджеты, которые должны быть использованы для каждого поля формы. Это словарь, где ключи - названия полей, а значения - классы виджетов.Например: поле
title
будет отображаться как текстовая область с 80 столбцами и 2 строками.class ArticleForm(ModelForm): class Meta: model = Article widgets = { 'title': Textarea(attrs={'cols': 80, 'rows': 2}) }
-
labels: Атрибут
labels
определяет человекочитаемые метки для каждого поля формы. Это словарь, где ключи - названия полей, а значения - строки с метками. Вообще если в модели для полей определён параметрverbose_name
, то будет использоваться это значение по умолчанию. Тоже самое касается параметраdefault
.Например: поле
title
будет отображаться с меткой “Заголовок”, а полеcontent
- с меткой “Содержание”.class ArticleForm(ModelForm): class Meta: model = Article labels = { 'title': 'Заголовок', 'content': 'Содержание', }
-
help_texts: Атрибут
help_texts
определяет вспомогательные тексты для каждого поля формы. Это словарь, где ключи - названия полей, а значения - строки с текстами подсказок. (Чтобы эта подсказка отображалась, шаблон должен использовать связанный с ней тег {{ field.help_text }})Например: рядом с полями
title
иcontent
будут отображаться соответствующие подсказки.class ArticleForm(ModelForm): class Meta: model = Article help_texts = { 'title': 'Введите краткий и информативный заголовок', 'content': 'Напишите подробное содержание статьи', }
-
error_messages: Атрибут
error_messages
определяет пользовательские сообщения об ошибках для каждого поля формы. Это словарь, где ключи - названия полей, а значения - словари с сообщениями об ошибках.Например: если длина заголовка превысит 200 символов или содержание статьи не будет заполнено, будут выведены соответствующие пользовательские сообщения об ошибках.
class ArticleForm(ModelForm): class Meta: model = Article error_messages = { 'title': { 'max_length': 'Заголовок не должен превышать 200 символов', }, 'content': { 'required': 'Содержание статьи является обязательным', }, }
-
field_classes: Атрибут
field_classes
определяет классы полей, которые должны быть использованы для каждого поля формы. Это словарь, где ключи - названия полей, а значения - классы полей.Например: поле
id
в формеArticleForm
будет представлено какIntegerField
, вместо стандартного поля модели.from django.forms.fields import IntegerField class ArticleForm(ModelForm): class Meta: model = Article field_classes = { 'id': IntegerField }
-
formfield_callable: Атрибут
formfield_callable
определяет функцию, которая будет вызываться для создания поля формы для каждого поля модели. Эта функция должна принимать модель, имя поля и опциональные аргументы и возвращать экземпляр поля формы.Например: функция
custom_formfield
используется для создания поля формы. Для поляtitle
она создаетCharField
с максимальной длиной 100 символов и обязательным заполнением, а для всех остальных полей использует стандартное поле, создаваемое моделью.def custom_formfield(model_field, **kwargs): if model_field.name == 'title': return forms.CharField(max_length=100, required=True) else: return model_field.formfield(**kwargs) class ArticleForm(ModelForm): class Meta: model = Article formfield_callable = custom_formfield
-
Доп. заметка, для отображения полей m2m в виде чекбоксов:
tags = forms.ModelMultipleChoiceField(queryset=Tags.objects.all(), **widget=forms.CheckboxSelectMultiple**)
Методы save() и save_m2m()
У формы, связанной с моделью по умолчанию существуют методы save()
и save_m2m()
в отличии от формы, которая ни с какой моделью не связана.
-
save() метод:
-
Основная функция - сохранение экземпляра модели, связанной с формой.
-
Вызывается, когда форма прошла валидацию и ее данные являются достоверными.
-
Возвращает сохраненный экземпляр модели.
-
Автоматически обрабатывает связанные поля (ForeignKey, OneToOneField), сохраняя их вместе с основной моделью.
-
Пример использования:
class ArticleForm(ModelForm): class Meta: model = Article fields = ['title', 'content'] article_form = ArticleForm(request.POST) if article_form.is_valid(): article = article_form.save() # article - сохраненный экземпляр модели Article
-
-
save_m2m() метод:
-
Предназначен для сохранения связей многие-ко-многим (ManyToManyField).
-
Вызывается после
save()
метода, так как при сохранении экземпляра модели связи Many-to-Many еще не были созданы. -
Не возвращает никакого значения, а просто сохраняет связи Many-to-Many.
-
Пример использования:
class ArticleForm(ModelForm): class Meta: model = Article fields = ['title', 'content', 'categories'] article_form = ArticleForm(request.POST) if article_form.is_valid(): article = article_form.save() article_form.save_m2m() # связи Many-to-Many между Article и Category теперь сохранены
-
Валидация данных
По сути эта часть практически ничем не отличается от описанной ранее для форм, которые с моделями не связаны. Единственное различие здесь в том, что можно определять логику для валидатора и на уровне модели, если форма связана с моделью. Например,
- Определение валидатора в модели:
-
Вы можете определить пользовательский валидатор в модели, используя метод
clean()
илиclean_<field_name>()
. -
Пример:
from django.core.exceptions import ValidationError from django.db import models class Article(models.Model): title = models.CharField(max_length=200) content = models.TextField() def clean_title(self): title = self.title if "bad word" in title.lower(): raise ValidationError("Заголовок не должен содержать нецензурные слова") return title
-
В этом примере мы определили пользовательский валидатор
clean_title()
, который проверяет, содержит ли заголовок статьи “нехорошие” слова.
-
- Использование валидатора в форме:
-
Когда вы создаете
ModelForm
, связанную с модельюArticle
, валидаторclean_title()
будет автоматически применяться к полюtitle
в форме. -
Пример:
from django.forms import ModelForm from .models import Article class ArticleForm(ModelForm): class Meta: model = Article fields = ['title', 'content']
-
Теперь, когда вы создадите экземпляр
ArticleForm
и вызоветеis_valid()
, валидаторclean_title()
из моделиArticle
будет автоматически применен к полюtitle
в форме. -
Если в заголовке будут обнаружены “нехорошие” слова, форма не пройдет валидацию, и вы сможете обработать ошибку в представлении.
-
Сравнение подходов к валидации данных:
- Валидаторы в модели:
- Централизация логики валидации: Определяя валидаторы непосредственно в модели, вы централизуете всю логику валидации в одном месте, что делает ваш код более организованным и легко поддерживаемым.
- Повторное использование: Если вы используете одну и ту же модель в нескольких формах, то валидаторы, определенные в модели, будут автоматически применяться ко всем этим формам, что уменьшает дублирование кода.
- Валидаторы в форме:
- Гибкость и специализация: Иногда логика валидации зависит от контекста использования формы. Определяя валидаторы в форме, вы можете более точно контролировать, какие валидаторы должны применяться в конкретном случае.
- Легкость тестирования форм: В некоторых случаях вам может быть удобнее тестировать логику валидации, когда она определена непосредственно в форме, так как это позволяет изолировать тесты для формы от остальной логики вашего приложения.
〰〰〰 𓆝 𓆟 𓆞 𓆝 𓆟 𓆝 𓆟 𓆞 〰〰〰