Django admin is good enough for admin to manage the contents based on the models. However, when you want the users of the website to manage their content, you need to create separate forms for them.
Introduction to the Django Form
Handling forms involves very complex logic:
- Prepare an HTML form.
- Validate fields in the browser using JavaScript or built-in HTML5 validation.
- Receive the values in the server.
- Validate fields in the server.
- Processing form values like saving them into the database if the form is valid
- Rerender the form with old values and an error message if the form is invalid.
Django forms simplify and automate almost all of the above steps.
Note that you can write code to do all of the above steps manually if you want more customization of the forms.
All forms in Django inherit from the django.forms.Form
class. The ModelForm
class allows you to create a form that associates with a model .
Defining a form
First, create a new file forms.py
in the blog
application’s directory.
Second, define a new form called PostForm
that inherits from the ModelForm
class:
from django.forms import ModelForm
from .models import Post
class PostForm(ModelForm):
class Meta:
model = Post
fields = ['title','content', 'author', 'author']
Code language: Python (python)
How it works.
- Import
ModelForm
from thedjango.forms
. - Import
Post
from themodels.py
module. - Define
PostForm
class that inherits from theModelForm
class. In thePostForm
class, define theMeta
class and specify themodel
andfields
attributes.
Third, define a route that displays the PostForm
:
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='posts'),
path('post/create', views.create_post, name='post-create'),
path('about/', views.about, name='about'),
]
Code language: Python (python)
Fourth, define create_post()
function that displays the form:
from django.shortcuts import render
from .models import Post
from .forms import PostForm
def create_post(request):
if request.method == 'GET':
context = {'form': PostForm()}
return render(request, 'blog/post_form.html', context)
def home(request):
posts = Post.objects.all()
context = {'posts': posts}
return render(request, 'blog/home.html', context)
def about(request):
return render(request, 'blog/about.html')
Code language: Python (python)
In the create_post()
, if the HTTP
request is GET
, then create a new instance of the PostForm
class and pass it to the render()
function.
Fifth, create the post_form.html
template:
{% extends 'base.html' %}
{% block content %}
<h2>New Post</h2>
<form method="post" novalidate>
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save" />
</form>
{% endblock content %}
Code language: HTML, XML (xml)
In the post_form.html
, add the csrf_token
tag and render the form using the form.as_p
property. It’ll output the following:
<p>
<label for="id_title">Title:</label>
<input type="text" name="title" maxlength="120" required id="id_title">
</p>
<p>
<label for="id_content">Content:</label>
<textarea name="content" cols="40" rows="10" required id="id_content"></textarea>
</p>
Code language: HTML, XML (xml)
If you open the URL http://127.0.0.1:8000/post/create
, you’ll see the following form:
If you click the Save button, you’ll see the error message:
Because the title, content, and author fields of the Post model are required fields by default, the PostForm
that uses the Post
model also renders an HTML form that requires these fields.
To test the server validation, you can disable the client validation by adding the novalidate
property to the form like this:
{% extends 'base.html' %}
{% block content %}
<h2>New Post</h2>
<form method="post" novalidate>
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save" />
</form>
{% endblock content %}
Code language: HTML, XML (xml)
To handle the HTTP POST method, you need to modify the create_post
function in the views.py
of the blog
application:
from django.shortcuts import render, redirect
from .models import Post
from .forms import PostForm
def create_post(request):
if request.method == 'GET':
context = {'form': PostForm()}
return render(request, 'blog/post_form.html', context)
elif request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
form.save()
return redirect('posts')
else:
return render(request, 'blog/post_form.html', {'form': form})
# ...
Code language: Python (python)
If the HTTP request is POST (request.method
==’POST
‘):
- Create a new instance of the
PostForm
class with the data from the POST. - Check if the form is valid.
- If the form is valid, save the form values into the database and redirect the web browser to the
'posts'
path. - Otherwise, rerender the form with old values and errors.
If you submit the form without entering anything values, you’ll get the following error messages:
However, when you provide values for some required fields, Django renders the form with old values and displays error messages for only invalid fields.
For example, the following form displays the error message for the title
field while retaining the old values for the content
and author
fields:
If you enter valid values for all the fields, Django saves the values into the database
…and redirect to the post list:
Summary
- Create a model form by subclassing the
ModelForm
. - Add the
novalidate
property to the form to disable HTML5 validation temporarily for testing server validation. - Use
form.is_valid()
to check if the form is valid. - Use
form.save()
to save form values into the database. - Use
redirect()
to redirect to a path.