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.
>>> 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 <Post: Post object> >>> form = PostForm(instance=post) >>> form.as_p() u'<p><label for="id_content">Content:</label> <input id="id_content" maxlength="256" name="content" type="text" value="New Post" /></p>\n<p><label for="id_created_at">Datetime created:</label> <input id="id_created_at" name="created_at" type="text" value="2013-08-14 21:12:30" /></p>'
- Python’s Django vs Ruby on Rails
- How to Install Django on Windows, Mac and Linux
- Python’s SQLAlchemy vs Other ORMs
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.
# 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, })
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.
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.
# 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'<p><label for="id_post">Post:</label> <select id="id_post" name="post">\n<option value="" selected="selected">---------</option>\n<option value="1">Post object</option>\n<option value="2">Post object</option>\n<option value="3">Post object</option>\n</select></p>\n<p><label for="id_message">Message:</label> <textarea cols="40" id="id_message" name="message" rows="10">\r\n</textarea></p>'
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.
# myblog/forms.py class PostModelForm(forms.ModelForm): class Meta: model = m.Post widgets = { 'content': forms.Textarea(attrs={'cols': 80, 'rows': 20}) }
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.
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.