Models And Forms
In the forms chapter we saw how to generate this form:
By using the following code in forms.py
:
class CourseForm(forms.Form):
name = forms.CharField(label="Name", max_length=128)
enrolled_students = forms.IntegerField(label="Students")
TIMES_OF_DAY = (
(1, "Morning"),
(2, "Afternoon"),
(3, "Evening")
)
time = forms.ChoiceField(label="Time", choices=TIMES_OF_DAY)
Now, compare how similar this form definition is to the definition of our Course
model in models.py
:
class Course(models.Model):
name = models.CharField(max_length=128)
enrolled_students = models.IntegerField()
TIMES_OF_DAY = (
(1, "Morning"),
(2, "Afternoon"),
(3, "Evening")
)
time = models.PositiveSmallIntegerField(choices=TIMES_OF_DAY, null=True)
Django provides a feature called ModelForm
to generate forms from models with the aim of avoiding code repetition: both of the fields and their respective types, and of their validations (max_length
, choices
, etc.)
So, let's replace the previous forms.py
code with this one:
from django.forms import ModelForm
from .models import Course
class CourseForm(ModelForm):
class Meta:
model = Course
fields = ["name", "enrolled_students", "time"]
The procedure to define a ModelForm
is simple:
- Create a class that inherits from
django.forms.ModelForm
. - Inside this class, create another one named
Meta
. - Inside
Meta
, specify themodel
from which you want to generate the form, and the list offields
of that model that you want the form to display.
That's enough for the form definition. What how do we process this form? Remember that the function in views.py
that we used to fetch the data submitted in CourseForm
and then to insert it into the Course
model was the following:
def new_course(request: HttpRequest) -> HttpResponse:
if request.method == "POST":
form = forms.CourseForm(request.POST)
if form.is_valid():
Course.objects.create(
name=form.cleaned_data["name"],
registered=form.cleaned_data["registered"]
)
return HttpResponseRedirect(reverse("courses"))
else:
form = forms.CourseForm()
ctx = {"form": form}
return render(request, "myapp/new_course.html", ctx)
Since CourseForm
is now a ModelForm
, in which we specified its source model as Course
, it is no longer necessary to fetch the submitted data manually via form.cleaned_data
nor to insert it in the model using Course.objects.create()
. Forms that inherit from ModelForm
will perfom this operations automatically when invoking the save()
method. So the previous view can be thus simplified:
def new_course(request: HttpRequest) -> HttpResponse:
if request.method == "POST":
form = forms.CourseForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse("courses"))
else:
form = forms.CourseForm()
ctx = {"form": form}
return render(request, "myapp/new_course.html", ctx)
Go to http://127.0.0.1:8000/myapp/new-course and test the new implementation:
Note that the label of each of the form fields ("Name", "Enrolled students", "Time") was built by Django from the names of the fields (name
, enrolled_students
, time
). This is useful during development, but generally you'll want to make those names explicit in the model definition:
class Course(models.Model):
name = models.CharField("Name", max_length=128)
enrolled_students = models.IntegerField("Students")
TIMES_OF_DAY = (
(1, "Morning"),
(2, "Afternoon"),
(3, "Evening")
)
time = models.PositiveSmallIntegerField("Time", choices=TIMES_OF_DAY, null=True)