================= Forms ================= Prefer ModelForm to Form -------------------------- 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 Hiding some fields from ModelForm which are needed for a DB save. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Eg, you want to create a profile for the logged in user.:: #in models.py class Profile(models.Model): user = models.OneToOneField(User) company = models.CharField(max_length=50) #in forms.py class ProfileForm(forms.ModelForm): class Meta: model = Profile fields = ['company',] #In views.py: form = ProfileForm(request.POST) profile = form.save(commit = False) profile.user = request.user profile.save() Or:: class ProfileForm(forms.ModelForm): class Meta: model = Profile fields =['company',] def __init__(self, user, *args, **kwargs) self.user = user super(ProfileForm, self).__init__(*args, **kwargs) def save(self, *args, **kwargs): self.instance.user = self.user super(ProfileForm, self).save(*args, **kwargs) Customizing widgets in ModelForm fields ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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 fields = ['picture', 'company'] 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() Saving multiple Objects in one form ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As:: class ProfileForm(forms.ModelForm): class Meta: model = Profile fields = ['company',] class UserForm(forms.ModelForm): class Meta: model = User fields = [...] #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 #}
{{ userform }} {{ profileform }}
Forms should know how to save themselves. --------------------------------------------- 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 The form should know what to do with it's data ------------------------------------------------ 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.