"
This article is part of in the series
Last Updated: Thursday 12th December 2013

Django's ModelForm

In our previous article, How to Use Python Django Forms, we learned how to create Django form objects and use them inside templates for common tasks such as data validation and HTML generation. In this article, we are going to learn how to use Django's ModelForm to create forms directly from models. Compared to a normal Django form object, a ModelForm object binds itself to a Django Model, thus relieving you of the responsibility to create a form object by hand.

Create a ModelForm for Model Post

In this next code snippet, we define, create and print a PostForm object.

[python]
>>> from myblog import models as m
>>> from django.forms import ModelForm
>>> class PostForm(ModelForm):
... class Meta:
... model = m.Post
...
>>> post = m.Post.objects.all()[0]
>>> post

>>> form = PostForm(instance=post)
>>> form.as_p()
u'

\n

'
[/python]

As you can see, the ModelForm object's as_p method returns exactly the same output as our previous form object's as_p method. Therefore, we can simply replace the form object in our post upload view with a ModelForm object.

[python]
# myblog/forms.py
...
from myblog import models as m

...

class PostModelForm(forms.ModelForm):
class Meta:
model = m.Post

# myblog/views.py
from myblog.forms import PostForm, PostModelForm

from myblog.forms import PostForm, PostModelForm

def post_form_upload(request):
if request.method == 'GET':
form = PostModelForm()
else:
# A POST request: Handle Form Upload
# Bind data from request.POST into a PostForm
form = PostModelForm(request.POST)
# If data is valid, proceeds to create a new post and redirect the user
if form.is_valid():
content = form.cleaned_data['content']
created_at = form.cleaned_data['created_at']
post = m.Post.objects.create(content=content, created_at=created_at)
return HttpResponseRedirect(reverse('post_detail', kwargs={'post_id': post.id}))

return render(request, 'post/post_form_upload.html', {
'form': form,
})
[/python]

Now you can refresh the page http://127.0.0.1:8000/post/form_upload.html to see that the new ModelForm object is rendered identically like the previous form object.

Our empty form page

Our empty form page.

Customize ModelForm objects

Instead of exposing all the fields of a model to the user, we can customize the list of fields to exclude certain database fields that we'd like to hide from the user. For example, Comment.created_at is probably a field whose value should default to django.utils.timezone.now() instead of being supplied by the user.

[python]
# myblog/forms.py
class CommentModelForm(forms.ModelForm):
class Meta:
model = m.Comment
exclude = ('created_at',)

# Python shell
>>> from myblog.forms import CommentModelForm
>>> form = CommentModelForm()
>>> form.as_p()
u'

\n

'
[/python]

Notice that Comment.created_at is not listed in the output from form.as_p.

Another type of customization we can perform is changing the default field types or widgets associated with certain model fields. For example, PostModelForm.content is rendered using a HTML input element, which is the default Django HTML widget for any CharField. We can change it to use a textarea element instead.

[python]
# myblog/forms.py
class PostModelForm(forms.ModelForm):
class Meta:
model = m.Post
widgets = {
'content': forms.Textarea(attrs={'cols': 80, 'rows': 20})
}
[/python]

Now you can refresh the page http://127.0.0.1:8000/post/form_upload.html and notice that the form field Content is rendered as a textarea element instead of a input element now.

With our textarea

With our textarea

Summary and Tips

In this article, we learned how to use and customize Django's ModelForm objects to expose a limited interface of a Django Model such as Post or Comment. Using ModelForm is more favorable than using Form because the former integrates Model objects tightly into its core functionality and relieves the programmer of some of the responsibility when creating a form by hand.

About The Author