Form khai báo field thủ công, không gắn với model — hợp với search, filter, contact form, multi-step wizard. ModelForm sinh field tự động từ model và có sẵn save() ghi DB — hợp với CRUD trực tiếp một model.
python
class ContactForm(forms.Form):
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea, max_length=2000)
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'body', 'tags'] # whitelist, KHÔNG dùng '__all__'
widgets = {'body': forms.Textarea(attrs={'rows': 10})}
def clean_title(self):
title = self.cleaned_data['title']
if Post.objects.filter(title=title).exists():
raise ValidationError('Title đã tồn tại')
return titleQuy tắc đơn giản: input không map 1-1 với model (nhiều bảng, field tính toán) → dùng Form. CRUD chuẩn một model → dùng ModelForm, tiết kiệm code và tự động đồng bộ với validator của model.
Cẩn thận: đừng bao giờ dùng fields = '__all__' ở ModelForm cho input người dùng — đó là cửa mở cho mass-assignment. Hôm nay model thêm field is_staff hay field internal nào đó là attacker có thể set giá trị qua form public mà bạn không hề biết.