ModelForm already know the correct UI widgets for your underlying Models. In most of the cases ModelForm would suffice instead of Forms.
Some common scenarios
Eg, you want to create a profile for the logged in user.:
#in Forms.py
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
exclude = ['user',]
#In Views:
form = ProfileForm(request.POST)
profile = form.save(commit = False)
profile.user = request.user
profile.save()
Or:
#Todo test this
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
exclude =['user',]
def __init__(self, user, *args, **kwargs)
self.user = user
super(ProfileForm, self).__init__(*args, **kwargs)
def save(*args, **kwargs):
self.instance.user = self.user
super(ProfileForm, self).save(*args, **kwargs)
Sometimes you just need to override the widget of a field that’s already on your ModelForm. Instead of duplicating the field definition (with help_text, required, max_length, etc). You can do this:
from django.contrib.admin.widgets import AdminFileWidget
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
exclude = 'user',
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
# note that self.fields is available just after calling super's __init__
self.fields['picture'].widget = AdminFileWidget()
As:
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
exclude = ['user',]
class UserForm(forms.ModelForm):
class Meta:
model = User
exclude = [...]
#in views.py
userform = UserForm(request.POST)
profileform = ProfileForm(request.POST)
if userform.is_valid() and profileform.is_valid():
#Only if both are valid together
user = userform.save()
profile = profileform.save(commit = False)
profile.user = user
profile.save()
{# In templates #}
<form ...>
{{ userform }}
{{ profileform }}
<input type="submit" />
</form>
if your forms is a forms.ModelForm, it already knows how to save its data. If you write a forms.Form, it should have a .save(). This keeps things symmetrical with ModelForms, and allows you to do:
#in views.py
def view_func(request):
if request.method == 'POST':
form = FormClass(request.POST)
if form.is_valid():
obj = form.save()
...
...
Instead of:
if form.is_valid():
#handle the saving in DB inside of views.
The .save() should return a Model Object
If you’re building a contact form, or something like this, the goal of your form is to send an email. So this logic should stay in the form:
class ContactForm(forms.Form):
subject = forms.CharField(...)
message = forms.TextField(...)
email = forms.EmailField(...)
...
def save(self):
mail_admins(self.cleaned_data['subject'], self.cleaned_data['message'])
I’ve used save(), and not send(), even when i’m not really saving anything. This is just a convention, people prefer to use save() to keep the same interface to ModelForms. But it doesn’t really matter, call it whatever you want.