Populate and Validate a Django ModelForm using API Data with the Generic CreateView

I ran into a slightly tricky use case the other day wherein I needed to grab some data from an API, validate it against a Django ModelForm, and only show the form to the user if there are errors during the validation.

If the form is shown, the error messages from the validation should appear along with a pre-populated form (using the data from the API). Otherwise, the ModelForm should submit and be saved immediately, without any user interaction.

After digging through Django’s code it became clear that to accomplish this, I needed to override a couple methods of the generic class-based CreateView.

Here’s the code for the view…

from django.core.urlresolvers import reverse_lazy
from django.views.generic import CreateView
from app.forms import MyModelForm
from app.lib.api import API
class MyCreateView(CreateView):
template_name = 'form.html'
form_class = MyModelForm
success_url = reverse_lazy('home')
object = None
# allow data to be passed to this
# pulled from django/views/generic/edit.py/ModelFormMixin
def get_form_kwargs(self, data=None):
kwargs = super(MyCreateView, self).get_form_kwargs()
# if we have data, set the form's data so that we can auto-submit this form (without POSTing)
if data:
kwargs.update({'initial': data, 'data': data})
return kwargs
def get(self, request, *args, **kwargs):
# grab the data from the API
data = API.get_data()
formdata = {'user_id': data['user_id'],
'email': data['email'],
'first_name': data['first_name'],
'last_name': data['last_name']}
form_class = self.get_form_class()
# must instantiate the class directly and include the data object rather than use get_form
# will set both the initial and instance data
#form = self.get_form(form_class)
form = form_class(**self.get_form_kwargs(formdata))
# do we have a valid form submission
if form.is_valid():
return self.form_valid(form)
return self.form_invalid(form)
# once the user submits the form, validate the form and create the new user
def post(self, request, *args, **kwargs):
# setup the form
# we can use get_form this time as we no longer need to set the data property
form_class = self.get_form_class()
form = self.get_form(form_class)
# grab the data from the API
data = API.get_data()
form.initial = {'user_id': data['user_id'],
'email': data['email'],
'first_name': data['first_name'],
'last_name': data['last_name']}
if form.is_valid():
return self.form_valid(form)
return self.form_invalid(form)

view raw


hosted with ❤ by GitHub

As you can see, the first thing I’m doing is overriding get_form_kwargs so that it can take a new argument – data. This allows me to pass in the data pulled from the API to the form. Since I want the form to be saved immediately, without any user interaction, I set both the initial and data properties to the passed in data.

The get method is now a simple exercise in pinging the API for data, placing it into a dictionary the form can use, and calling form.is_valid() to see if the data passes the validation.

If it doesn’t, the form is rendered (complete with validation errors and pre-populated data) and the post method handles when the form is submitted by the user.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s