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в форме. -
Если в заголовке будут обнаружены “нехорошие” слова, форма не пройдет валидацию, и вы сможете обработать ошибку в представлении.
-
Сравнение подходов к валидации данных:
- Валидаторы в модели:
- Централизация логики валидации: Определяя валидаторы непосредственно в модели, вы централизуете всю логику валидации в одном месте, что делает ваш код более организованным и легко поддерживаемым.
- Повторное использование: Если вы используете одну и ту же модель в нескольких формах, то валидаторы, определенные в модели, будут автоматически применяться ко всем этим формам, что уменьшает дублирование кода.
- Валидаторы в форме:
- Гибкость и специализация: Иногда логика валидации зависит от контекста использования формы. Определяя валидаторы в форме, вы можете более точно контролировать, какие валидаторы должны применяться в конкретном случае.
- Легкость тестирования форм: В некоторых случаях вам может быть удобнее тестировать логику валидации, когда она определена непосредственно в форме, так как это позволяет изолировать тесты для формы от остальной логики вашего приложения.
〰〰〰 𓆝 𓆟 𓆞 𓆝 𓆟 𓆝 𓆟 𓆞 〰〰〰