Django has some really great forms. Their Forms API is rich, reusable, and generally a pleasure to work with. Many things in web development start with accepting input from the user, so having this in your toolkit is a great productivity boost when developing. Django also has ModelForms which allow a very simple form to be created and validated based on a model’s fields. This enables rapid prototype development.
One catch I ran into early on is combining ForeignKey
relations with various ModelForm instances. Django’s admin site has a whole separate pop-up window that will take care of this for you, but that’s a little invasive and frankly too old school 1 for our applications. My default previously was to use a ForeignKey that defaulted to None
and then force a separate page that handled filling in the new model.
Forget all that dumbassery.
Django forms (yes, ModelForm instances too) allow you to prefix field names by instantiating them in a constructor. Without too much more talk, let’s dive into some code examples.
# Initalize our two forms here with separate prefixes
form = SchoolForm(prefix="sch")
sub_form = LocationForm(prefix="loc")
# Check to see if a POST has been submitted. Using GET to submit forms?
# Don't do it. Use POST.
if request.POST:
# Load up our two forms again using the prefix keyword argument.
form = SchoolForm(request.POST, prefix="sch")
sub_form = LocationForm(request.POST, prefix="loc")
# Ensure both forms are valid before continuing on
if form.is_valid() and sub_form.is_valid():
# Prepare the school model, but don't commit it to the database
# just yet.
school = form.save(commit=False)
# Add the location ForeignKey by saving the secondary form we
# setup
school.location = sub_form.save()
# Save the main object and continue on our merry way.
school.save()
return _goto_school(school)
# Continue request processing and handle rendering whatever template
In the template we can do something like this.
<form method="POST">
<h2>School Information:</h2>
<!-- Make use of Django's automatic form templating -->
{{ form.as_p }}
<!-- Now add the sub-form to this form -->
<h3>Location Information:</h3>
{{ sub_form.as_p }}
<p><button type="submit">Add School</button></p>
</form>
-
We’ve actually had clients complain that the admin site looks “like it was pulled straight from 1998.” ↩︎