开发者

Why does known valid Django model instance fail is_valid() after retrieval from database?

开发者 https://www.devze.com 2022-12-22 01:30 出处:网络
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 throu

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.


form.is_valid() verifies the form.data dict, which is sent via the constructor for Form(data=request.POST)

ModelForm.instance associates the data with a particular table row, so that a save necessarily performs an update and not an insert. This is also passed via the constructor.

Both these are however, independent of each other. If you want to create a form with old instance's data you should do the following:

ToolForm(data=oldinstance.__data__, instance=oldinstance)

However, you perhaps don't really want to bind the data right away.

ToolForm(instance=oldinstance)

populates the right values from the instance, when rendered in the html and update the record, only if ToolForm instance is_changed()


I'm probably misunderstanding something but it looks to me that your analyze function should not take form.cleaned_data as input but rather dataset_id.

In case the example is not complete — why are you creating a form out of a dataset in order to analyze it?


We changed the argument for the analyze function to be a model instance instead of form.cleaned_data. This decouples the analysis from form validation and is just much more sensible. Conceptually, the second part of the code given above now looks like this now:

if (request.method != 'POST') and (dataset_id):
        oldset = get_object_or_404(ToolDataset, pk=dataset_id)
        form = ToolForm(instance=oldset)
        results = analyze(oldset)

Of course, the unpacking of the data at the head of the analyze function had to be re-written somewhat.

This all seems obvious now. Thank you, everyone, for your interest!

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号