Tuesday, July 31, 2012

Get the View's request object as self.request in your Form Subclass!!!


http://stackoverflow.com/questions/1057252/how-do-i-access-the-request-object-or-any-other-variable-in-a-forms-clean-met

Again, like in many other Django things, you must have access to pass in arguments to the constructor of a Django object, AND THEN, have access to override the __init__ method of that Django object, so that it can take in and process your constructor argument as part of its self.





Scenario,
you have a Form/ModelForm subclass, which needs to do validation on some View request variable.

you want to access in your Form subclass, it as self.request


Q.
I am trying to request.user for a form's clean method, but how can I access the request object? Can I modify the clean method to allow variables input?



A much better way is to override the form's __init__ method to take an extra keyword argument,request. This stores the request in the form, where it's required, and from where you can access it in your clean method.
class MyForm(forms.Form):

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(MyForm, self).__init__(*args, **kwargs)


    def clean(self):
        ... access the request object via self.request ...
and in your view:
myform = MyForm(request.POST, request=request)








How to process form-inputted data in a View -- is_valid() then form.cleaned_data['formfieldname']


https://docs.djangoproject.com/en/1.4/topics/forms/



Processing the data from a form

Once is_valid() returns True, you can process the form submission safe in the knowledge that it conforms to the validation rules defined by your form. 




if form.is_valid():
    subject = form.cleaned_data['subject']
    message = form.cleaned_data['message']
    sender = form.cleaned_data['sender']
    cc_myself = form.cleaned_data['cc_myself']

    recipients = ['info@example.com']
    if cc_myself:
        recipients.append(sender)

    from django.core.mail import send_mail
    send_mail(subject, message, sender, recipients)
    return HttpResponseRedirect('/thanks/') # Redirect after POST



While you could access request.POST directly at this point, it is better to accessform.cleaned_data. This data has not only been validated but will also be converted in to the relevant Python types

data = getattr(request, request.method)
a = data.get('formfieldname')
b = data.get('post_or_get_variable_name')


###############################################

https://docs.djangoproject.com/en/dev/topics/forms/


Processing the data from a form

Once is_valid() returns True, the successfully validated form data will be in the form.cleaned_data dictionary. This data will have been converted nicely into Python types for you.
Note
You can still access the unvalidated data directly from request.POST at this point, but the validated data is better.
In the above example, cc_myself will be a boolean value. Likewise, fields such as IntegerField and FloatField convert values to a Python int and float respectively.
Read-only fields are not available in form.cleaned_data (and setting a value in a custom clean() method won't have any effect). These fields are displayed as text rather than as input elements, and thus are not posted back to the server.
Extending the earlier example, here's how the form data could be processed:
if form.is_valid():
    subject = form.cleaned_data['subject']
    message = form.cleaned_data['message']
    sender = form.cleaned_data['sender']
    cc_myself = form.cleaned_data['cc_myself']

    recipients = ['info@example.com']
    if cc_myself:
        recipients.append(sender)

    from django.core.mail import send_mail
    send_mail(subject, message, sender, recipients)
    return HttpResponseRedirect('/thanks/') # Redirect after POST









Changing values populated to a form (works for unbound ModelForms and Forms only)


http://stackoverflow.com/questions/813418/django-set-field-value-after-a-form-is-initialized

The asker's scenario:


I am trying to set the field to a certain value after the form is initialized.
For example, I have the following class.



####################



Since you're not passing in POST data, I'll assume that what you are trying to do is set an initial value that will be displayed in the form. The way you do this is with the initial keyword.
form = CustomForm(initial={'Email': GetEmailString()})
See the Django Form docs for more explanation.
If you are trying to change a value after the form was submitted, you can use something like:
if form.is_valid():
    form.cleaned_data['Email'] = GetEmailString()
Check the referenced docs above for more on using cleaned_data





##############################
Only the first method -- unbound form constructed, then the initial data passed in or set using field.initial -- works in populating the field.

If after is_valid() is called, it doesn't work to populate the field shown in the browser to the user:
what this does is just change the value inside the cleaned_data dict in your view. (for internal ss-processing, perhaps.)











Redirection is good after Form submit, to prevent "Reload button - double submission" problem.


http://stackoverflow.com/questions/5823580/django-form-resubmitted-upon-refresh


After successful submission and processing of a web form, you need to use a returnHttpResponseRedirect, even if you are only redirecting to the same view. Otherwise, certain browsers (I'm pretty sure FireFox does this) will end up submitting the form twice.



Here's an example of how to handle this...
def some_view(request):
  if request.method == "POST":
    form = some_form(request.POST)
    if form.is_valid():
      # do processing
      # save model, etc.
      return HttpResponseRedirect("/some/url/")
  return render_to_response("normal/template.html", {"form":form}, context_instance=RequestContext(request))


###############
The asker's scenario:


After I submit the form for the first time and then refresh the form it gets resubmitted and and I don't want that.
Here's my form in template :

The Standard Way of using a form in a field (when to call constructors, when to construct bound with arguments, when to redirect)


https://docs.djangoproject.com/en/dev/topics/forms/


Using a form in a view

The standard pattern for processing a form in a view looks like this:
from django.shortcuts import render
from django.http import HttpResponseRedirect

def contact(request):
    if request.method == 'POST': # If the form has been submitted...
        form = ContactForm(request.POST) # A form bound to the POST data
        if form.is_valid(): # All validation rules pass
            # Process the data in form.cleaned_data
            # ...
            return HttpResponseRedirect('/thanks/') # Redirect after POST
    else:
        form = ContactForm() # An unbound form

    return render(request, 'contact.html', {
        'form': form,
    })



2

Form submitted?Data?What occurs
UnsubmittedNone yetTemplate gets passed unbound instance of ContactForm.
SubmittedInvalid dataTemplate gets passed bound instance of ContactForm.
SubmittedValid dataValid data is processed. Redirect to a "thanks" page.



3

The distinction between Bound and unbound forms is important:
  • An unbound form has no data associated with it. When rendered to the user, it will be empty or will contain default values.
  • A bound form has submitted data, and hence can be used to tell if that data is valid. If an invalid bound form is rendered, it can include inline error messages telling the user what data to correct.








Unbound Forms vs. Bound Forms - The difference is in the constructor arguments.


https://docs.djangoproject.com/en/dev/ref/forms/api/




BoundField.errors
A list-like object that is displayed as an HTML <ul class="errorlist"> when printed:
>>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''}
>>> f = ContactForm(data, auto_id=False)


data var is as above.


BoundField.value()
New in Django 1.3: Please see the release notes
Use this method to render the raw value of this field as it would be rendered by a Widget:


>>> initial = {'subject': 'welcome'}
>>> unbound_form = ContactForm(initial=initial)
>>> bound_form = ContactForm(data, initial=initial)
>>> print(unbound_form['subject'].value())
welcome
>>> print(bound_form['subject'].value())
hi


unbound form
bound form takes argument dict "data".



Template Tags How-To in Django : Comments, Tags, Titles


http://www.b-list.org/weblog/2006/jun/07/django-tips-write-better-template-tags/



Monday, July 30, 2012

How to add formatted per-field errors : Override the .clean() method of a Django Form/ModelForm.

Only can do it this way, and not anymore in the View function. -__-"

Scenario:

You used JavaScript to selectbox:onchange() to:
1. insert a <input hidden name="sth" value="nth"> hidden field with name="sth" and value="nth".
2. submit POST the Django form.

3. You want to show a form field's errmsg.
You have to override the form object's .clean() method (called on a call to is_valid() in the view),
BUT, you can't get the request object in that method. SO:

# You can't get the request object in form.clean method.
# So just try to 'get' from cleaned_data, your inserted <input hidden> from the form.


    def clean(self):
        cleaned_data = super(ProductEditForm, self).clean()

##        print "cleaned data EditForm", cleaned_data
        # No choice, even non-form request.GET or POST['hidden_lang_change'] data has to be checked here.
        # If you want to even put anything in self.field._errors = [some list of errmsgs,]

        print "===" # You can't get the request object in form.clean method.
        print cleaned_data.get('lang') # So just try to 'get' from cleaned_data, your inserted <input hidden> from the form.
        print cleaned_data.get('lancau')
        print "---"

        if cleaned_data.get('hidden_lang_change'):
            pass
        else:
            # purposely set field error
            errmsg = "purposely set field error"
            self._errors["lang"] = self.error_class([errmsg])
            pass

        return cleaned_data



##############################EXAMPLE PRINTOUT:
in the View, this line: print "is_valid()=", self.product_edit_form.is_valid()

Gives the below printout:


is_valid()= ===
en
None
---
False


=== : call to form.is_valid() starts. The '===' prints out first.
The '---' prints out last.
The actual return value of is_valid is printed; it is False. because of the form error that you purposely made.

Cleaned-up Form Submit/Repopulate program flow code snippet.

Same as previous post, but this is a clean skeleton, the comment clutter and line breaks cleaned up.



        self.build_breadcrumbs() #TODO: SLOTTED IN THIS HERE.
        #----------------------------------------------------

        if request.method == 'POST':
            # Define the form structure first. (population args=the POST data, db instance.)
            # Design in any custom (non-model) fields and their choices.
            self.product_edit_form = ProductEditForm(self.get_submit_data(), instance=self.product)
            self.customize_product_edit_form(self.product_edit_form)

            if not data.get('hidden_lang_change'): # If is a 'REAL' submit,
                if self.product_edit_form.is_valid():
                    # Repopulate the form: Modify .cleaned_data[key] and pass back in to form constructor.
                    self.product_edit_form.cleaned_data['description'] = "NEW DESC query from Property Table"
                    self.product_edit_form = ProductEditForm(self.product_edit_form.cleaned_data, instance=self.product)
                    print "lalalalaa"
                else:
                    # Handle form validation error,
                    # either self.error('msg')
                    # and/or return redirect(...)
                    self.product_edit_form.fields['description'].help_text = "zzzzzzzzzZZZZZZ"
                    print "efgh"
            else: # A 'language change submit': repopulate some fields from the Property table AFTER creating the form
                print "wxyz"
                pass
            print "submitted"
            # if errors, you end up at the same form here again. With "this field is required" error.
            # if no errors, you also end up submitted here UNLESS you "return redirect ..." -- but with no error messages on the form!
        else:
            self.product_edit_form = ProductEditForm(instance=self.product)
            self.customize_product_edit_form(self.product_edit_form)
            print "new form"




Here's the complete re-population of form code snippet.

Scenario:

You have two modes of form submit,

1
one is the 'real' submit, where you submit everything to be written to the db
-- triggered on the submit button.

2
the other is the 'fake submit', which a javascript adds a hidden field into the form,
if you find that, you don't write to db,
but you re-construct the form (call form constructor!) clobber some of the modelfields with your own values.
To do that, construct a dict from the form.cleaned_data, change some of the values thus
form.cleaned_data['somekey'] = "data queried from fi language Property table"
and call the form constructor with these arguments:
form = SomeForm(form.cleaned_data, instance=self.product)

Then, at the end of the def request() method,
it falls out to the API,
which returns the current template
(i.e. that is to say, don't return redirect(...), if you want to show the newly populated form again, at the same template.)
##################################################




    def request(self, request, *args, **kwargs):
        def populate_form(form_info):
            form_name = form_info.name
            form_cls = form_info.cls
            form = self._create_form(form_info)
            setattr(self, form_name, form)
            method_name = 'populate_' + form_name
            try:
                self.call_method(method_name, form)
            except CalledMethodNotFound:
                request_logger.debug('Not calling {0}, method not found'.format(method_name))

        self.request = request
        self.view_args = args
        self.view_kwargs = kwargs

        data = getattr(request, request.method)
        self.form_name = data.get('frm')
        self.button_name = data.get('btn')

        submitted_form_info = None

        self.build_breadcrumbs() #TODO: SLOTTED IN THIS HERE.

##        if request.method == 'POST':
##            self.product_edit_form = ProductEditForm(request.POST)
##            if self.product_edit_form.is_valid():
##                print "ok"
##                # Process the data in form.cleaned_data
##                # ...
##                #return self.redirect_product_edit(self.operator_id, self.product_id)
##        else:
##            self.product_edit_form = ProductEditForm(self.get_submit_data(), instance=self.product)
##            print "pk"
##
##        #return self.redirect_product_edit(self.operator_id, self.product_id)






        # for forms in collection
        # form_name = "product_edit_form"
        # self.form_name = data.get('frm')

        # if form_name = submitted_data("frm") # if this was the form that was submitteed

        ## Just create the fucking form with the fucking name, regardless of request.POST or not.
        ## do nothing else.
##        self.product_edit_form = ProductEditForm(self.get_submit_data(), instance=self.product) #OK
        ##EXAMPLE---

        if request.method == 'POST':
            self.product_edit_form = ProductEditForm(self.get_submit_data(), instance=self.product)
            self.create_product_edit_form()


            if not data.get('hidden_lang_change'): # A 'REAL' submit
                if self.product_edit_form.is_valid():
                    print "asdf", self.product_edit_form.cleaned_data
                    #TEST: A 'language change submit': repopulate some fields from the Property table AFTER creating the form
                    self.product_edit_form.fields['description'].help_text = "AFAVSVAAAS"
                    #self.product_edit_form.fields['description'].initial = "---AFAVSVAAAS---"
                    self.product_edit_form.cleaned_data['description'] = "NEW DESC"

                    self.product_edit_form = ProductEditForm(self.product_edit_form.cleaned_data, instance=self.product)
                    print "lalalalaa"
                else:
                    self.product_edit_form.fields['description'].help_text = "zzzzzzzzzZZZZZZ"


                print "efgh"
            else: # A 'language change submit': repopulate some fields from the Property table AFTER creating the form
                pass




            print "submitted"
            # if errors, you end up at the same form here again. With "this field is required" error.
            # if no errors, you also end up submitted here unless you "return redirect ..." -- but with no error messages on the form!
        else:
            self.create_product_edit_form()
            print "new form"





##        for form_info in self.form_infos:
##            form_name = form_info.name
##
##            if form_name == self.form_name:
##                submitted_form_info = form_info
##                if form_info.readonly:
##                    populate_form(form_info)
##            else:
##                populate_form(form_info)
##
##        if self.form_name:
##            if not submitted_form_info:
##                raise ValueError('Invalid form {0}'.format(self.form_name))
##
##            if not submitted_form_info.readonly:
##                response = self._submit_form(submitted_form_info)
##                if response:
##                    return response
##        else:
##            audit_logger.info('%s', self.__class__.__name__)



    def create_product_edit_form(self):
        self.product_edit_form = ProductEditForm(self.get_submit_data(), instance=self.product)
        self.product_edit_form.fields['lang'].choices = [
            (code, get_localized_desc(code)) for code in LANGUAGE_CODES
        ]
        self.product_edit_form.fields['lang'].help_text = '(Select language to refresh fields to the new language)'









REMEMBER: To construct the form EXACTLY with populating the non-auto ModelForm populated fields.
Else, fields.clean() will complain onSubmit.





        if request.method == 'POST':
            self.product_edit_form = ProductEditForm(self.get_submit_data(), instance=self.product)
            self.create_product_edit_form()


            if not data.get('hidden_lang_change'): # A 'REAL' submit
                if self.product_edit_form.is_valid():
                    print "asdf", self.product_edit_form.cleaned_data
                    #TEST: A 'language change submit': repopulate some fields from the Property table AFTER creating the form
                    self.product_edit_form.fields['description'].help_text = "AFAVSVAAAS"
                    #self.product_edit_form.fields['description'].initial = "---AFAVSVAAAS---"
                    self.product_edit_form.cleaned_data['description'] = "NEW DESC"

                    self.product_edit_form = ProductEditForm(self.product_edit_form.cleaned_data, instance=self.product)
                    print "lalalalaa DON'T RETURN REDIRECT(...)"
                else:
                    self.product_edit_form.fields['description'].help_text = "zzzzzzzzzZZZZZZ"


                print "efgh"
            else: # A 'language change submit': repopulate some fields from the Property table AFTER creating the form
                pass












Handling Forms in Django, Twice-and-for-all (more form lifecycle, where and when can you still change stuff)



        if request.method == 'POST':
            self.product_edit_form = ProductEditForm(self.get_submit_data(), instance=self.product)
            self.create_product_edit_form()


            if not data.get('hidden_lang_change'): # A 'REAL' submit
                ##print "asdf", self.product_edit_form.cleaned_data ## FAILS: .cleaned_data attr, does not exist before call to is_valid()

                if self.product_edit_form.is_valid():
                    print "asdf", self.product_edit_form.cleaned_data
                #TEST: A 'language change submit': repopulate some fields from the Property table AFTER creating the form
                    self.product_edit_form.fields['description'].help_text = "AFAVSVAAAS"
                else:
                    self.product_edit_form.fields['description'].help_text = "zzzzzzzzzZZZZZZ"


                print "efgh"
            else: # A 'language change submit': repopulate some fields from the Property table AFTER creating the form
                pass




            print "submitted"
            # if errors, you end up at the same form here again. With "this field is required" error.
            # if no errors, you also end up submitted here unless you "return redirect ..." -- but with no error messages on the form!
        else:
            self.create_product_edit_form()
            print "new form"
##################################





Scenario:
So, you could change the HELP TEXT before the response rendering is returned,
but can you CHANGE THE VALUES INSIDE the <input> boxes? -- answer is, YES.







Handling Forms in Django, Once-and-for-all

    def request(self, request, *args, **kwargs):


        self.request = request
        self.view_args = args
        self.view_kwargs = kwargs

        data = getattr(request, request.method)
        self.form_name = data.get('frm')
        self.button_name = data.get('btn')


        self.build_breadcrumbs() #TODO: SLOTTED IN THIS HERE.
###################


        if request.method == 'POST':
            self.product_edit_form = ProductEditForm(self.get_submit_data(), instance=self.product)
            self.product_edit_form.fields['lang'].choices = [
                (code, get_localized_desc(code)) for code in LANGUAGE_CODES
            ]
            self.product_edit_form.fields['lang'].help_text = '(Select language to refresh fields to the new language)'
            # Construct the EXACT entire form again - if not, it will complain "en is not one of the available choices"       Select a valid choice. en is not one of the available choices.

            print "submitted"
            # if errors, you end up at the same form here again. With "this field is required" error.
            # if no errors, you also end up submitted here unless you "return redirect ..." -- but with no error messages on the form!
        else:
            self.create_product_edit_form()
            print "new form"








Friday, July 27, 2012

ModelForms with custom non-model fields (trying to preserve field ordering)





class ProductAddForm(forms.ModelForm):
    """ Make a form with extra fields, to save as well to property table.
        Operator-specific Customization Params:


        1. URL field for MobSec only download page
        2. URL field for System Requirements (Both MobSec and IS products.)


        Caveats:
        Enforce saving to property table only if key is unique!
    """
    # Custom non-model fields: First thing, lang dropdown box first.
    lang = forms.ChoiceField()
    operator = ReadOnlyField()


    def __init__(self, *args, **kwargs):
        """ Smart way to take care of reordering fields with custom non-model fields,
            without having to explicitly state every field in Meta.fields.
            http://djangosnippets.org/snippets/759/


            Although it may be overkill here but sometimes we don't (want to) know all the other
            fields in the modelform beforehand and type them out in fields=(,) explicitly,
            OR we wanted to preserve the exact fields order in the existing ModelForm.


            desired_order : desired order of the new fields (go by key names)
            The key names are the names of the non-model fields that you defined in this ModelForm (look above.)
        """
        super(ProductAddForm, self).__init__(*args, **kwargs)
        desired_order = ('lang',)
        desired_list = []
        for key in desired_order:
            for k in self.fields.keys()[:]:
                if k==key:
                    val = self.fields.pop(k)
                    desired_list.append((key,val)) # desired_list.append({ key: val })


        print "1=", val
        print "2=", self.fields
        print "3=", desired_list


##        for item in desired_list[::-1]:
##            self.fields.insert(0, item) #REMINDER NOTE: DON'T USE .insert it is deprecated.


        self.fields = SortedDict(desired_list + self.fields.items()) # https://code.djangoproject.com/wiki/SortedDict
        print "4=", "done"


    class Meta:
        model = Product
##        widgets = {
##            'operator': ReadOnlyField(),
##        }