Web to py enterprise web framework - p 21 ppt

10 192 0
Web to py enterprise web framework - p 21 ppt

Đang tải... (xem toàn văn)

Thông tin tài liệu

FORM 185 Hidden fields When the above form object is serialized by {{=form}}, and because of the previous call to the accepts method, it now looks like this: 1 <form enctype="multipart/form-data" action="" method="post"> 2 your name: 3 <input name="name" /> 4 <input type="submit" /> 5 <input value="783531473471" type="hidden" name="_formkey" /> 6 <input value="default" type="hidden" name="_formname" /> 7 </form> Notice the presence of two hidden fields: " formkey" and " formname". Their presence is triggered by the call to accepts and they play two different and important roles: • The hidden field called " formkey" is a one-time token that web2py uses to prevent double submission of forms. The value of this key is generated when the form is serialized and stored in the session. When the form is submitted this value must match, or else accepts returns False without errors as if the form was not submitted at all. This is because web2py cannot determine whether the form was submitted correctly. • The hidden field called " formname" is generated by web2py as a name for the form, but the name can be overridden. This field is neces- sary to allow pages that contain and process multiple forms. web2py distinguishes the different submitted forms by their names. 186 FORMS AND VALIDATORS The role of these hidden fields and their usage in custom forms and pages with multiple forms is discussed in more detail later in the chapter. If the form above is submitted with an empty "name" field, the form does not pass validation. When the form is serialized again it appears as: 1 <form enctype="multipart/form-data" action="" method="post"> 2 your name: 3 <input value="" name="name" /> 4 <div class="error">cannot be empty!</div> 5 <input type="submit" /> 6 <input value="783531473471" type="hidden" name="_formkey" /> 7 <input value="default" type="hidden" name="_formname" /> 8 </form> Notice the presence of a DIV of class "error" in the serialized form. web2py inserts this error message in the form to notify the visitor about the field that did not pass validation. The accepts method, upon submission, determines that the form is submitted, checks whether the field "name" is empty and whether it is required, and eventually inserts the error message from the validator into the form. The base "layout.html" view is expected to handle DIVs of class "error". The default layout uses jQuery effects to make errors appear and slide down with a red background. See Chapter 10 for more details. keepvalues The full signature of the accepts method is the following: 1 form.accepts(vars, session=None, formname='default', 2 keepvalues=False, onvalidation=None): The optional argument keepvalues tells web2py what to do when a form is accepted and there is no redirection, so the same form is displayed again. By default the form is cleared. If keepvalues is set to True, the form is prepopulated with the previously inserted values. This is useful when you have a form that is supposed to be used repeatedly to insert multiple similar records. onvalidation The onvalidation argument can be None or can be a function that takes the form and returns nothing. Such a function would be called and passed the form, immediately after validation (if validation passes) and before anything else happens. The purpose of this function is multifold. It can be used, for example, to perform additional checks on the form and eventually add errors FORM 187 to the form. It can also be used to compute the values of some fields based on the values of other fields. It can be used to trigger some action (like sending an email) before a record is created/updated. Here is an example: 1 db.define_table('numbers', 2 Field('a', 'integer'), 3 Field('b', 'integer'), 4 Field('d', 'integer', readable=False, writable=False)) 5 6 def my_form_processing(form): 7 c = form.vars.a * form.vars.b 8 if c < 0: 9 form.errors.b = 'a * b cannot be negative' 10 else: 11 form.vars.c = c 12 13 def insert_numbers(): 14 form = SQLFORM(db.numbers) 15 if form.accepts(request.vars, session, 16 onvalidation=my_form_processing) 17 session.flash = 'record inserted' 18 redirect(request.url) 19 return dict(form=form) Forms and redirection The most common way to use forms is via self-submission, so that the submitted field variables are processed by the same action that generated the form. Once the form is accepted, it is unusual to display the current page again (something we are doing here only to keep things simple). It is more common to redirect the visitor to a "next" page. Here is the new example controller: 1 def display_form(): 2 form = FORM('Your name:', 3 INPUT(_name='name', requires=IS_NOT_EMPTY()), 4 INPUT(_type='submit')) 5 if form.accepts(request.vars, session): 6 session.flash = 'form accepted' 7 redirect(URL(r=request, f='next')) 8 elif form.errors: 9 response.flash = 'form has errors' 10 else: 11 response.flash = 'please fill the form' 12 return dict(form=form) 13 14 def next(): 15 return dict() 188 FORMS AND VALIDATORS In order to set a flash on the next page instead of the current page you must use session.flash instead of response.flash. web2py moves the former into the latter after redirection. Note that using session.flash requires that you do not session.forget(). Multiple forms per page The content of this section applies to both FORM and SQLFORM objects. It is possible to have multiple forms per page, but you must allow web2py to distinguish them. If these are derived by SQLFORM from different tables, then web2py gives them different names automatically; otherwise you need to explicitlygivethemdifferentform names. Moreover, whenmultiple formsare present on the same page, the mechanism for preventing double submission breaks, and you must omit the session argument when calling the accepts method. Here is an example: 1 def two_forms(): 2 form1 = FORM(INPUT(_name='name', requires=IS_NOT_EMPTY()), 3 INPUT(_type='submit')) 4 form2 = FORM(INPUT(_name='name', requires=IS_NOT_EMPTY()), 5 INPUT(_type='submit')) 6 if form1.accepts(request.vars, formname='form_one'): 7 response.flash = 'form one accepted' 8 if form2.accepts(request.vars, formname='form_two'): 9 response.flash = 'form two accepted' 10 return dict(form1=form1, form2=form2) and here is the output it produces: When the visitor submits an empty form1, only form1 displays an error; if the visitor submits an empty form2, only form2 displays an error message. SQLFORM 189 No self-submission The content of this section applies to both FORM and SQLFORM objects. What we discuss here is possible but not recommended, since it is always good practice to have forms that self-submit. Sometimes, though, you don’t have a choice, because the action that sends the form and the action that receives it belong to different applications. It is possible to generate a form that submits to a different action. This is done by specifying the URL of the processing action in the attributes of the FORM or SQLFORM object. For example: 1 form = FORM(INPUT(_name='name', requires=IS_NOT_EMPTY()), 2 INPUT(_type='submit'), _action=URL(r=request, f='page_two')) 3 4 def page_one(): 5 return dict(form=form) 6 7 def page_two(): 8 if form.accepts(request.vars, formname=None): 9 response.flash = 'form accepted' 10 else: 11 response.flash = 'there was an error in the form' 12 return dict() Notice that since both "page one" and "page two" use the same form, we have defined it only once by placing it outside of all the actions, in order not to repeat ourselves. The common portion of code at the beginning of a controller gets executed every time before giving control to the called action. Since "page one" does not call accepts, the form has no name and no key, so you must not pass the session and set formname=None in accepts, or the form will not validate when "page two" receives it. 7.2 SQLFORM We now move to the next level by providing the application with a model file: 1 db = DAL('sqlite://db.db') 2 db.define_table('person', 3 Field('name', requires=IS_NOT_EMPTY())) Modify the controller as follows: 1 def display_form(): 2 form = SQLFORM(db.person) 3 if form.accepts(request.vars, session): 4 response.flash = 'form accepted' 5 elif form.errors: 6 response.flash = 'form has errors' 7 else: 190 FORMS AND VALIDATORS 8 response.flash = 'please fill out the form' 9 return dict(form=form) The view does not need to be changed. In the new controller, you do not need to build a FORM, since the SQLFORM constructor built one from the table db.person defined in the model. This new form, when serialized, appears as: 1 <form enctype="multipart/form-data" action="" method="post"> 2 <table> 3 <tr id="person_name__row"> 4 <td><label id="person_name__label" 5 for="person_name">Your name: </label></td> 6 <td><input type="text" class="string" 7 name="name" value="" id="person_name" /></td> 8 <td></td> 9 </tr> 10 <tr id="submit_record__row"> 11 <td></td> 12 <td><input value="Submit" type="submit" /></td> 13 <td></td> 14 </tr> 15 </table> 16 <input value="9038845529" type="hidden" name="_formkey" /> 17 <input value="person" type="hidden" name="_formname" /> 18 </form> The automatically generated form is more complex than the previous low- level form. First of all, it contains a table of rows, and each row has three columns. The first column contains the field labels (as determined from the db.person), the second column contains the input fields (and eventually error messages), and the third column is optional and therefore empty (it can be populated with the fields in the SQLFORM constructor). All tags in the form have names derived from the table and field name. This allows easy customization of the form using CSS and JavaScript. This capability is discussed in more detail in Chapter 10. More important is that now the accepts method does a lot more work for you. As in the previous case, it performs validation of the input, but additionally, if the input passes validation, it also performs a database insert of the newrecord and stores in form.vars.id the unique "id" of the newrecord. A SQLFORM object also deals automatically with "upload" fields by saving uploaded files in the "uploads" folder (after having them renamed safely to avoid conflicts and prevent directory traversal attacks) and stores their names (their new names) into the appropriate field in the database. A SQLFORM displays "boolean" values with checkboxes, "text" values with textareas, values required to be in a definite set or a database with dropboxes, and "upload" fields with links that allow users to download the uploaded files. SQLFORM 191 It hides "blob" fields, since they are supposed to be handled differently, as discussed later. For example, consider the following model: 1 db.define_table('person', 2 Field('name', requires=IS_NOT_EMPTY()), 3 Field('married', 'boolean'), 4 Field('gender', requires=IS_IN_SET(['Male', 'Female', 'Other'])), 5 Field('profile', 'text'), 6 Field('image', 'upload')) In this case, SQLFORM(db.person) generates the form shown below: The SQLFORM constructor allows various customizations, such as displaying only a subset of the fields, changing the labels, adding values to the op- tional third column, or creating UPDATE and DELETE forms, as opposed to INSERT forms like the current one. SQLFORM is the single biggest time-saver object in web2py. The class SQLFORM is defined in "gluon/sqlhtml.py". It can be easily ex- tended by overloading its xml method, the method that serializes the objects, to change its output. The signature for the SQLFORM constructor is the following: 1 SQLFORM(table, record=None, deletable=False, 2 linkto=None, upload=None, fields=None, labels=None, col3={}, 192 FORMS AND VALIDATORS 3 submit_button='Submit', delete_label='Check to delete:', 4 id_label='Record id: ', showid=True, 5 readonly=False, comments=True, keepopts=[], 6 ignore_rw=False, ** attributes) • The optional second argument turns theINSERT form into an UPDATE form for the specified record (see next subsection). • If deletable is set to True, the UPDATE form displays a "Check to delete" checkbox. The value of the label if this field is set via the delete label argument. • submit button sets the value of the submit button. • id label sets the label of the record "id" • The "id" of the record is not shown if showid is set to False. • fields is an optional list of field names that you want to display. If a list is provided, only fields in the list are displayed. For example: 1 fields = ['name'] • labels is a dictionary of field labels. The dictionary key is a field name and the corresponding value is what gets displayed as its label. If a label is not provided, web2py derives the label from the field name (it capitalizes the field name and replaces underscores with spaces). For example: 1 labels = {'name':'Your Full Name:'} • col3 is a dictionary of values for the third column. For example: 1 col3 = {'name':A('what is this?', 2 _href='http://www.google.com/search?q=define:name')} • linkto and upload are optional URLs to user-defined controllers that allow the form to deal with reference fields. This is discussed in more detail later in the section. • readonly. If set to True, displays the form as readonly • comments. If set to False, does not display the col3 comments • ignore rw. Normally, for a create/update form, only fields marked as writable=True are shown, and for readonly forms, only fields marked as readable=True are shown. Setting ignore rw=True causes those con- straints to be ignored, and all fields are displayed. This is mostly used SQLFORM 193 in the appadmin interface to display all fields for each table, overriding what the model indicates. • Optional attributes are arguments starting with underscore that you want to pass to the FORM tag that renders the SQLFORM object. Examples are: 1 _action = '.' 2 _method = 'POST' There is a special hidden attribute. When a dictionary is passed as hidden, its items are translated into "hidden" INPUT fields (see the example for the FORM helper in Chapter 5). Insert/Update/Delete SQLFORM If you pass a record as optional second argument to the SQLFORM constructor, the form becomes an UPDATE form for that record. This means that when the form is submitted the existing record is updated and no new record is inserted. If you set the argument deletable=True, the UPDATE form displays a "check to delete" checkbox. If checked, the record is deleted. You can, for example, modify the controller of the previous example so that when we pass an additional integer argument in the URL path, as in: 1 /test/default/display_form/2 and if there is a record with the corresponding id, the SQLFORM generates an UPDATE/DELETE form for the record: 1 def display_form(): 2 if len(request.args): 3 records = db(db.person.id==request.args[0]).select() 4 if len(request.args) and len(records): 5 form = SQLFORM(db.person, records[0], deletable=True) 6 else: 7 form = SQLFORM(db.person) 8 if form.accepts(request.vars, session): 9 response.flash = 'form accepted' 10 elif form.errors: 11 response.flash = 'form has errors' 12 return dict(form=form) Line 3 finds the record, line 5 makes an UPDATE/DELETE form, and line 7 makes an INSERT form. Line 8 does all the corresponding form processing. Here is the final page: 194 FORMS AND VALIDATORS By default deletable=False. Edit forms also contain a hidden INPUT field with name="id" which is used to identify the record. This id is also stored server-side for additional security and, if the visitor tampers with the value of this field, the UPDATE is not performed and web2py raises a SyntaxError, "user is tampering with form". When a Field is marked with writable=False, the field is not shown in create forms, and it is is shown readonly in update forms. If a field is marked as writable=False and readable=False, then the field is not shown at all, not even in update forms. Forms created with 1 form = SQLFORM( ,ignore_rw=True) ignore the readable and writable attributes and always show all fields. Forms in appadmin ignore them by default. Forms created with 1 form = SQLFORM(table,record_id,readonly=True) always show all fields in readonly mode, and they cannot be accepted. SQLFORM in HTML There are times when you want to use SQLFORM to benefit from its form generation and processing, but you need a level of customization of the form in HTML that you cannot achieve with the parameters of the SQLFORM object, so you have to design the form using HTML. Now, edit the previous controller and add a new action: . Field('gender', requires=IS_IN_SET(['Male', 'Female', 'Other'])), 5 Field('profile', 'text'), 6 Field('image', 'upload')) In. session.forget(). Multiple forms per page The content of this section applies to both FORM and SQLFORM objects. It is possible to have multiple forms per page, but you must allow web2 py to distinguish. created/updated. Here is an example: 1 db.define_table('numbers', 2 Field('a', 'integer'), 3 Field('b', 'integer'), 4 Field('d', 'integer',

Ngày đăng: 06/07/2014, 19:20

Tài liệu cùng người dùng

Tài liệu liên quan