Form cleaned_data

219 views
Skip to first unread message

Timothy W. Cook

unread,
Nov 24, 2013, 8:46:41 AM11/24/13
to django...@googlegroups.com
I have an issue with save data from a form.

The actual form has one required and two optional fields.
The model has three additional fields that I want to assign based on
the context, two of those are ForeignKey related.

The post method in the view:
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)

if form.is_valid():
# <process form cleaned data>
paper = get_object_or_404(Paper,pk=kwargs['pk'])
reviewer =
Reviewer.objects.filter(user=self.request.user).filter(prj_name=paper.project)
form.cleaned_data['reviewer'] = reviewer[0]
form.cleaned_data['paper'] = paper
form.cleaned_data['stage'] = paper.current_stage
print(form.cleaned_data)
form.save()
return HttpResponseRedirect('#')
return render(request, self.template_name, {'form': form})

The form:
class ReviewTitleCreateForm(ModelForm):
class Meta:
model = Review
fields = ['include','exclusion_choice','exclusion_text',]


The model:
class Review(models.Model):
"""
A review at a specific stage of one paper.
"""
INCLUDE_CHOICES = [(True,'Include'),(False,'Exclude')]
STAGES = [('Selection by Title','Selection by Title'),('Selection
by Abstract','Selection by Abstract'),
('Selection by Full Text','Selection by Full Text')]

reviewer = models.ForeignKey(Reviewer, verbose_name=_('Reviewer'),
related_name="%(app_label)s_%(class)s_related", null=False,
blank=False, help_text=_("The reviewer of this paper, at this
stage."))
paper = models.ForeignKey(Paper, verbose_name=_('Paper'),
related_name="%(app_label)s_%(class)s_related", null=False,
blank=False, help_text=_("The reviewed paper."))
review_stage = models.CharField("Review Stage",max_length=30,
choices=STAGES, help_text="The stage for review of this paper.")
include = models.NullBooleanField("Choice",
choices=INCLUDE_CHOICES, default=True, null=False, blank=False,
help_text="Select Include or Exclude for this stage of review.")
exclusion_choice = models.ForeignKey(Exclusion, null=True,
blank=True, related_name="%(app_label)s_%(class)s_related")
exclusion_text = models.TextField(null=True, blank=True)


When I output the cleaned_data from the print at the bottom of the
post method (see above), I get this:
{'reviewer': <Reviewer: Tim Cook>, 'include': True, 'paper': <Paper:
Assessing the quality of clinical data in a computer-based record for
calculating the pneumonia severity index.>, 'exclusion_text': '',
'stage': 'Selection by Title', 'exclusion_choice': None}


Which looks correct, to me. However I am getting an error as if there
is no Reviewer assigned:
IntegrityError at /papers/review_title/3
null value in column "reviewer_id" violates not-null constraint
Request Method:POST

Any ideas?

Thanks,
Tim









--
MLHIM VIP Signup: http://goo.gl/22B0U
============================================
Timothy Cook, MSc +55 21 94711995
MLHIM http://www.mlhim.org
Like Us on FB: https://www.facebook.com/mlhim2
Circle us on G+: http://goo.gl/44EV5
Google Scholar: http://goo.gl/MMZ1o
LinkedIn Profile:http://www.linkedin.com/in/timothywaynecook

Timothy W. Cook

unread,
Nov 24, 2013, 1:10:22 PM11/24/13
to django...@googlegroups.com
SOLVED: (I think)

Though I do not understand the how/why. From an example on
StackOverflow I tried changing the post method to this:

def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)

if form.is_valid():
# <process form cleaned data>
paper = get_object_or_404(Paper,pk=kwargs['pk'])
reviewer =
Reviewer.objects.filter(user=self.request.user).filter(prj_name=paper.project)
f = form.save(commit=False)
f.reviewer = reviewer[0]
f.paper = paper
f.stage = paper.current_stage
f.save()
return HttpResponseRedirect('#')

return render(request, self.template_name, {'form': form})


and it works. Now to study more about saving the form to a new object
and then adding the additional data before saving with a commit. Very
confusing ...

Daniel Roseman

unread,
Nov 24, 2013, 1:38:17 PM11/24/13
to django...@googlegroups.com
Hmm, I tried to post an answer to your original question earlier but it never appeared.

The reason why adding the values to `cleaned_data` doesn't work is that those fields aren't in the `fields` list for the form in the first place, so it doesn't know to do anything with them. Adding them to the uncommitted model instance is the correct solution.
--
DR.

Timothy W. Cook

unread,
Nov 24, 2013, 1:46:21 PM11/24/13
to django...@googlegroups.com
On Sun, Nov 24, 2013 at 4:38 PM, Daniel Roseman <dan...@roseman.org.uk> wrote:
> Hmm, I tried to post an answer to your original question earlier but it never appeared.
>

Thanks Daniel. Your help is very much appreciated.

> The reason why adding the values to `cleaned_data` doesn't work is that those fields aren't in the `fields` list for the form in the first place, so it doesn't know to do anything with them. Adding them to the uncommitted model instance is the correct solution.
> --

I guess I was thinking that cleaned_data is a dictionary sent to the
ORM for commit?

Now, what is confusing is why saving the 'form' instance to another
(in this case 'f') instance without a commit and adding the values to
the new instance makes a difference. Isn't 'f' just a copy of 'form'?

Any clarification?

Thanks,
Tim



> DR.
>
> --
> You received this message because you are subscribed to the Google Groups "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
> To post to this group, send email to django...@googlegroups.com.
> Visit this group at http://groups.google.com/group/django-users.
> To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/d0ad9a93-ffbf-4439-a06a-270c965cfb6a%40googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.

Daniel Roseman

unread,
Nov 25, 2013, 5:22:40 AM11/25/13
to django...@googlegroups.com
On Sunday, 24 November 2013 18:46:21 UTC, Timothy W. Cook wrote:
On Sun, Nov 24, 2013 at 4:38 PM, Daniel Roseman <dan...@roseman.org.uk> wrote:
> Hmm, I tried to post an answer to your original question earlier but it never appeared.
>

Thanks Daniel.  Your help is very much appreciated.

> The reason why adding the values to `cleaned_data` doesn't work is that those fields aren't in the `fields` list for the form in the first place, so it doesn't know to do anything with them. Adding them to the uncommitted model instance is the correct solution.
> --

I guess I was thinking that cleaned_data is a dictionary sent to the
ORM for commit?

Well, not really, it only takes account of those fields that are in Meta.fields (and not in Meta.exclude).
 
Now, what is confusing is why saving the 'form' instance to another
(in this case 'f') instance without a commit and adding the values to
the new instance makes a difference.  Isn't 'f' just a copy of 'form'?

Any clarification?

No, `f` is now an unsaved instance of the Review model, rather than a form (you should probably make that clearer by calling the instance `r` or `review`). So you can modify it like you would any other model instance before calling save() - and of course this is the normal model save(), not the form's.
--
DR.

Timothy W. Cook

unread,
Nov 25, 2013, 4:42:51 PM11/25/13
to django...@googlegroups.com
On Mon, Nov 25, 2013 at 8:22 AM, Daniel Roseman <dan...@roseman.org.uk> wrote:
> On Sunday, 24 November 2013 18:46:21 UTC, Timothy W. Cook wrote:
>>
>> On Sun, Nov 24, 2013 at 4:38 PM, Daniel Roseman <dan...@roseman.org.uk>
>> wrote:

>
> No, `f` is now an unsaved instance of the Review model, rather than a form
> (you should probably make that clearer by calling the instance `r` or
> `review`).

I will certainly call it something else now that I realize:
f = form.save(commit=False)
means to make a copy of the model without saving it to disk. I
previously (from the SO example) thought I was making a copy of he
form.

IT still isn't clear how form.save yeailds a copy of the model and not
a copy of the form.

> So you can modify it like you would any other model instance
> before calling save() - and of course this is the normal model save(), not
> the form's.

So the follow on to that is how (if 'f' is a model instance) does the
form data get into my model instance. I am sure that there is
somewhere in the docs or at least in the code that this can be seen?

Thanks,
Tim

Daniel Roseman

unread,
Nov 26, 2013, 6:34:44 AM11/26/13
to django...@googlegroups.com
On Monday, 25 November 2013 21:42:51 UTC, Timothy W. Cook wrote:
On Mon, Nov 25, 2013 at 8:22 AM, Daniel Roseman <dan...@roseman.org.uk> wrote:
> On Sunday, 24 November 2013 18:46:21 UTC, Timothy W. Cook wrote:
>>
>> On Sun, Nov 24, 2013 at 4:38 PM, Daniel Roseman <dan...@roseman.org.uk>
>> wrote:

>
> No, `f` is now an unsaved instance of the Review model, rather than a form
> (you should probably make that clearer by calling the instance `r` or
> `review`).

I will certainly call it something else now that I realize:
            f = form.save(commit=False)
means to make a copy of the model without saving it to disk.  I
previously (from the SO example) thought I was making a copy of he
form.

IT still isn't clear how form.save yeailds a copy of the model and not
a copy of the form.

> So you can modify it like you would any other model instance
> before calling save() - and of course this is the normal model save(), not
> the form's.

So the follow on to that is how (if 'f' is a model instance) does the
form data get into my model instance.  I am sure that there is
somewhere in the docs or at least in the code that this can be seen?

But that's just what form.save() does. In your original code, you called it and a new Review object was saved in the database (it was also returned, but you didn't use it). The only difference here is that you've added the commit=False parameter, which does everything it did before except actually saving to the database.
--
DR.

Timothy W. Cook

unread,
Nov 27, 2013, 4:50:39 AM11/27/13
to django...@googlegroups.com
Apologies for being DENSE:

Partial code from the post:

def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)

if form.is_valid():
# <process form cleaned data>
paper = get_object_or_404(Paper,pk=kwargs['pk'])
reviewer =
Reviewer.objects.filter(user=self.request.user).filter(prj_name=paper.project)
f = form.save(commit=False)
...

So are you saying what I called 'form' above is NOT an instance of a
the form from the post? IT is actually an instance of the model?

Thanks,
Tim

Tom Evans

unread,
Nov 27, 2013, 5:13:25 AM11/27/13
to django...@googlegroups.com
No, "form" is an instance of your form. Model forms have a method
called save(), when you call this method, the model form updates an
instance of the model with the values from the form, saves the
instance and then returns it to the caller. If a model instance is
passed to the model form when it is constructed, then that instance is
updated, otherwise a fresh model instance is created.

So, "form" is a form, but "f" is not a form, it is a model instance.

https://docs.djangoproject.com/en/1.5/topics/forms/modelforms/#the-save-method

Cheers

Tom

Timothy W. Cook

unread,
Nov 27, 2013, 5:38:31 AM11/27/13
to django...@googlegroups.com
Thanks Tom.

On Wed, Nov 27, 2013 at 8:13 AM, Tom Evans <teva...@googlemail.com> wrote:
>
> No, "form" is an instance of your form. Model forms have a method
> called save(), when you call this method, the model form updates an
> instance of the model with the values from the form, saves the
> instance and then returns it to the caller.

Yep, this was the part that I wasn't 'getting'.

Cheers,
Tim
Reply all
Reply to author
Forward
0 new messages