views:

2417

answers:

1

Thanks to Insin for answering a previous question related to this one.

His answer worked and works well, however, I'm perplexed at the provision of 'cleaned_data', or more precisely, how to use it?

class RegistrationFormPreview(FormPreview):
    preview_template    = 'workshops/workshop_register_preview.html'
    form_template       = 'workshops/workshop_register_form.html'

    def done(self, request, cleaned_data):
        # Do something with the cleaned_data, then redirect
        # to a "success" page. 

        registration            = Registration(cleaned_data)
        registration.user       = request.user
        registration.save()
        # an attempt to work with cleaned_data throws the error: TypeError 
        # int() argument must be a string or a number, not 'dict'
        # obviously the fk are python objects(?) and not fk_id
        # but how to proceed here in an easy way?



        # the following works fine, however, it seems to be double handling the POST data
        # which had already been processed in the django.formtools.preview.post_post
        # method, and passed through to this 'done' method, which is designed to 
        # be overidden.
        '''
        form                    = self.form(request.POST)   # instansiate the form with POST data
        registration            = form.save(commit=False)   # save before adding the user
        registration.user       = request.user              # add the user
        registration.save()                                 # and save.
        '''

        return HttpResponseRedirect('/register/success')

For quick reference, here's the contents of the post_post method:

def post_post(self, request):
    "Validates the POST data. If valid, calls done(). Else, redisplays form."
    f = self.form(request.POST, auto_id=AUTO_ID)
    if f.is_valid():
        if self.security_hash(request, f) != request.POST.get(self.unused_name('hash')):
            return self.failed_hash(request) # Security hash failed.
        return self.done(request, f.cleaned_data)
    else:
        return render_to_response(self.form_template,
            {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state},
            context_instance=RequestContext(request))
+5  A: 

I've never tried what you're doing here with a ModelForm before, but you might be able to use the ** operator to expand your cleaned_data dictionary into the keyword arguments expected for your Registration constructor:

   registration = Registration (**cleaned_data)

The constructor to your model classes take keyword arguments that Django's Model meta class converts to instance-level attributes on the resulting object. The ** operator is a calling convention that tells Python to expand your dictionary into those keyword arguments.

In other words...

What you're doing currently is tantamount to this:

registration = Registration ({'key':'value', ...})

Which is not what you want because the constructor expects keyword arguments as opposed to a dictionary that contains your keyword arguments.

What you want to be doing is this

registration = Registration (key='value', ...)

Which is analogous to this:

registration = Registration (**{'key':'value', ...})

Again, I've never tried it, but it seems like it would work as long as you aren't doing anything fancy with your form, such as adding new attributes to it that aren't expected by your Registration constructor. In that case you'd likely have to modify the items in the cleaned_data dictionary prior to doing this.

It does seem like you're losing out on some of the functionality inherent in ModelForms by going through the form preview utility, though. Perhaps you should take your use case to the Django mailing list and see if there's a potential enhancement to this API that could make it work better with ModelForms.

Edit

Short of what I've described above, you can always just extract the fields from your cleaned_data dictionary "by hand" and pass those into your Registration constructor too, but with the caveat that you have to remember to update this code as you add new fields to your model.

registration = Registration (
    x=cleaned_data['x'],
    y=cleaned_data['y'],
    z=cleaned_data['z'],
    ...
)
Joe Holloway
gee, it's so easy when you get good help! well done and thanks... I had wondered what those ** were for! (it was the only mod needed btw, adding the **)
Antonius Common
If all you are trying to do is create a model based on form data (which is what it looks like), why not just use a ModelForm and be done?
Carl Meyer
@Carl To clarify, he *is* using a ModelForm, but the intermediate use of FormPreview prevents you from using certain features of said ModelForm (object construction and commit) without redundant processing. This is why I suggested taking his use case to the Django mailing list.
Joe Holloway