We have a Django Model, ToolDataset, and a ModelForm, ToolForm. In the Model each instance, or database row, is referred to as a dataset, and the primary key is called the dataset_id. First time through, the user fills out an unbound form and submits it. Conceptually, this is the view code that is used to validate, save and analyze the dataset:
if (request.method == 'POST') and (not dataset_id):
form = ToolForm(request.POST)
if form.is_valid():
new_dataset = form.save()
dataset_id = new_dataset.pk
results = analyze(form.cleaned_data)
else:
<validation error code>
I think so far this is very normal. Note that form data aren't saved and no dataset_id is assigned unless the data are valid.
Now some time passes, and the user wants to return to this particular old dataset, perhaps to change the data and re-analyze it. So, by whatever means, a URL is composed that looks like www.finesite.com/Tool/X/, where X is the dataset_id corresponding to the particular row of data the user wants to work with. Through the URLconf, a different branch of the view code is invoked, that we thought ought to look like this:
if (request.method != 'POST') and (dataset_id):
oldset = get_object_or_404(ToolDataset, pk=dataset_id)
form = ToolForm(instance=oldset)
if form.is_valid():
results = analyze(form.cleaned_data)
else:
<validation error code that we expected would never run>
Well, as it turns out, this dataset, that was valid when we stored it, doesn't validate now, which is a surprise. We used the manage.py shell to inspect the form a bit. Here's some of what we found:
>>> form.is_valid()
False
>>> form.errors
{}
>>> form.non_field_errors()
[]
>>> form.is_bound
False
Running form.as_p()
yields what seems to be a complete form.
A very capable associate found an undocumented API function known as model_to_dict() in django/forms/models.py. He suggested substituting this,
form = BXEEP_L_Form(model_to_dict(oldset), instance=oldset),
for this,
form = BXEEP_L_Form(instance=oldset).
It works now - the form is valid and bound, according to the shell - but I am full of questions. Why does this work? Why is this necessary? Is there a more standard way of doing this? It seems odd to have to use an undocumented internal function for a use case that seems so commonplace and uncomplicated.