Using Forms¶
A Simple Form¶
This is the core of Yota’s functionality. To create a Form with Yota you must inherit from the From superclass like in the following example.
from yota import Form
from yota.nodes import *
class PersonalForm(Form):
first = EntryNode()
last = EntryNode()
address = EntryNode()
submit = SubmitNode(title="Submit")
Forms are simply a collection of Nodes and Checks. The Checks drive validation of the Form and will be talked about next, while the Nodes drive rendering. Conceptually Nodes can be thought of as a single input section in your form, but it can actually be anything that is destined to generate some HTML or Javascript in your Form. For example you may wish to place a header at the beginning to the Form even though it isn’t used for any data entry. Most keyword arguments passed to a Node are passed directly to their rendering context, and thus their use is completely up to user choice. More information on Nodes can be found in the Nodes documentation section. Your new Form class inherits lots of functionality for common tasks such as rendering and validation.
To render our Form we can call the Form.render()
function on an instance of our Form object:
>>> personal = PersonalForm()
>>> personal.render()
'<form method="post">
...
</form>'
As talked about in the Node documentation, each Node by default has an
associated template that is used to render it. The render function essentially
passes the list of Nodes in the Form onto the Renderer. Most renderers will
render each each Node’s template and append them all together. In addition to
the Nodes that you have defined in your subclass, a Node for the beginning and
end of your Form will automatically be injected. The is a convenience that can
be disabled by setting the Form.auto_start_close
to False. We
can see this functionality in action in the below example:
>>> form = PersonalForm()
>>> for node in form._node_list:
... print node._attr_name
...
start
first
last
address
submit
close
Even though ‘first’ was our first element in the Form and ‘submit’ was our last,
the Nodes ‘start’ and ‘close’ have been prepended and appended respectively. By
default these Nodes load from templates ‘form_open.html’ and ‘form_close.html’,
however these values can be easily overridden, as can the entire start and
close Nodes. For more information see the Form.auto_start_close
,
Form.start_template
, Form.close_template
. Passing in a ‘start’
or ‘close’ attribute, either through keywords or subclass attributes, will
override the default generated Nodes, but it will still place them at the
beginning and end.
Validation Intro¶
To add some validation to our Form we need to create a Check
. Checks are just containers for Validators and hold information about how the Validator should be executed. The below code will add a Check for the ‘first’ Node
to ensure a minimum length of 5 characters.
from yota import Form
from yota.nodes import *
from yota.validation import
class PersonalForm(Form):
first = EntryNode()
_first_valid = Check(MinLengthValidator(5), 'first')
last = EntryNode()
address = EntryNode()
submit = SubmitNode(title="Submit")
The constructor prototype may help provide some reference for the explaination:
When you define a Check object you are essentially specifying a Validator that needs to be run when the Form data is validated, and the information that needs to be passed to said Validator. Attr_args and attr_kwargs should be strings that define what data will get passed into the Validator at validation time. For instance in the above example that data that was entered for the ‘first’ Node will get passed to the validator. More information on Checks and Validators can be found on the Validators and Checks page.
Dynamic Forms¶
One of the key features of Yota is the ability to make changes to the Form schema at runtime with little effort. For example, say you wanted to make a Form that allowed the user to enter a list of names, and the form included a button that added another field with JavaScript. Or perhaps you would like to create a Form that is slightly different depending on session data. With a dynamic Form schema managing these situations can be much easier.
Since the Form object that is used to run validation after a submission needs to match the Form object that was used to originally render the Form there are some considerations that need to be made. There are of course many ways to try and solve this synchronization problem, but here is a straightforward solution that should apply to most situations.
This section currently needs expansion, however a thoroughly commented example can be found in the yota_examples github repository.
Form API¶
-
class
yota.
Form
(**kwargs)[source]¶ This is the base class that all user defined forms should inherit from, and as such it is the main way to access functionality in Yota. It provides the core functionality involved with setting up and rendering the form.
Parameters: - context – This is a context specifically for the special form open and form close nodes, canonically called start and close.
- g_context – This is a global context that will be passed to all nodes in rendering thorugh their rendering context as ‘g’ variable.
- start_template – The template used when automatically
injecting a start Node. See
yota.Form.auto_start_close
for more information. - close_template – The template used when automatically
injecting a close Node. See
yota.Form.auto_start_close
for more information. - auto_start_close – Dictates whether or not start and close Nodes will be automatically appended/prepended to your form. Note that this must be set via __init__ or your class definition since it must be set before __init__ for the Form is run.
- hidden – A dictionary of hidden key/value pairs to be injected into the form. This is frequently used to pass dynamic form parameters into the validator.
-
_event_lists
= {}¶
-
_gen_validate
(data, piecewise='auto', internal=False, resolver=None)¶ Runs all the validators associated with the
Form
.Returns: Whether validation was successful
-
_node_list
= []¶
-
_parse_shorthand_validator
(node)[source]¶ Loops thorugh all the Nodes and checks for shorthand validators. After inserting their checks into the form obj they are removed from the node. This is because a validation may be called multiple times on a single form instance.
-
_processor
¶ This is a class that performs post processing on whatever is passed in as data during validation. The intended purpose of this was to write processors that translated submitted form data from the format of the web framework being used to a format that Yota expects. It also allows things like filtering stripping characters or encoding all data that enters a validator.
alias of
FlaskPostProcessor
-
_renderer
¶ This is a class object that is used to perform the actual rendering steps, allowing different rendering engines to be swapped out. More about this in the section
Renderer
alias of
JinjaRenderer
-
_reserved_attr_names
= ('context', 'hidden', 'g_context', 'start_template', 'close_template', 'auto_start_close', '_renderer', '_processor', 'name')¶
-
_setup_node
(node)[source]¶ An internal function performs some safety checks, sets attribute, and set_identifiers
-
_submit_action
= False¶ Tracks whether you’re submitting the form, or just validating it for later json serialization
-
_success_data
= None¶ Hold information that will be serialized into success return values for render_json
-
_validation_list
= []¶
-
auto_start_close
= True¶
-
close_template
= 'form_close'¶
-
context
= {}¶
-
data_by_attr
()[source]¶ Returns a dictionary of currently stored
Node.data
attributes keyed byNode._attr_name
. Used for returning data after its been processed by validators.
-
data_by_name
()[source]¶ Returns a dictionary of currently stored
Node.data
attributes keyed byNode.name
. Used for returning data after its been processed by validators.
-
error_header_generate
(msgs)[source]¶ This function is generally used to generate a header on the start Node automatically when there is an error in validation. For instance, you might want to say “Please fix the errors below” or something similar. While it could actually be used for anything post-validation failure, it is better practice to create a listener that subscribes to “validation_failure” event, as this function is called at the same time.
Parameters: msgs (list) – This will be a list of all other Nodes that have messages, with the idea that you might want to list the errors that occurred.
-
g_context
= {}¶
-
get_by_attr
(name)[source]¶ Safe accessor for looking up a node by
Node._attr_name
-
insert_listener
(listener)[source]¶ Attaches a
Listener
to an event type. These Listener will be executed when trigger event is called.
-
insert_node
(position, new_node_list)[source]¶ Inserts a
Node
object or a list of objects at the specified position into theForm._node_list
of the form. Index -1 is an alias for the end of the list. After insertion theNode.set_identifiers()
will be called to generate identification for theNode
. For this to function,Form._attr_name
must be specified for the node prior to insertion.
-
insert_node_after
(prev_attr_name, new_node_list)[source]¶ Runs through the internal node structure attempting to find a
Node
object whosNode._attr_name
is prev_attr_name and inserts the passed node after it. If prev_attr_name cannot be matched it will be inserted at the end. Internally callsForm.insert()
and has the same requirements of theNode
.Parameters:
-
insert_validator
(new_validators)[source]¶ Inserts a validator to the validator list.
Parameters: validator (Check) – The Check
to be inserted.
-
name
= None¶
-
pysistor_adapter
= None¶
-
pysistor_backend
= None¶ This defines an adapter object that will be made availible to the Pysistor backend for use in storing the data. For instance, access to sessions frequently requires access to the request object and an adapter can carry that information. More information on this behaviour can be gotten in the pysistor documentation
-
render
()[source]¶ Runs the renderer to parse templates of nodes and generate the form HTML.
Returns: A string containing the generated output.
-
render_error
= False¶
-
render_json
(invalid=None, success=None, raw=False)[source]¶ This function takes the state that is stored internally and serializes it into a form that the yota JS library is designed to recieve.
-
render_success
= False¶
-
resolve_all
(data)[source]¶ This is a utility method that runs resolve_data on all nodes with the provided data dictionary.
-
set_json_success
(**kwargs)[source]¶ As opposed to using generate_json_success to pass information to the js success function you can use add_success in your view code.
-
start_template
= 'form_open'¶
-
success_json_generate
()[source]¶ Please see the documentation for
Form.error_header_generate()
as it covers this function as well as itself.
-
title
= None¶
-
type_class_map
= {'info': 'alert alert-info', 'warn': 'alert alert-warn', 'success': 'alert alert-success', 'error': 'alert alert-error'}¶ A mapping of error types to their respective class values. Used to render messages to the user from validation. Changing it to render messages differently could be performed as follows:
class MyForm(yota.Form): first = EntryNode(title='First name', validators=Check(MinLengthValidator(5))) last = EntryNode(title='Last name', validators=MinLengthValidator(5) # Override the default type_class_map with our own type_class_map = {'error': 'alert alert-error my-special-class', # Add an additional class 'info': 'alert alert-info', 'success': 'alert alert-success', 'warn': 'alert alert-warn'}
-
validate
(data, piecewise='auto', internal=False, resolver=None)[source]¶ Runs all the validators associated with the
Form
.Returns: Whether validation was successful
-
validate_json
(data, piecewise='auto', raw=False)[source]¶ The same as
Form.validate_render()
except the messges are loaded into a JSON string to be passed back as a query result. This output is designed to be used by the Yota Javascript library.Parameters: - piecewise – This parameter is deprecated. Piecewise is automatically detected from g_context.
- raw (boolean) – If set to True then the second return parameter will be a Python dictionary instead of a JSON string
Returns: A boolean whether or not the form submission is valid and the json string (or raw dictionary) to pass back to the javascript side. The boolean is an anding of submission (whether the submit button was actually pressed) and the block parameter (whether or not any blocking validators passed)
-
validate_render
(data)[source]¶ Runs all the validators on the data that is passed in and returns a re-render of the
Form
if there are validation message, otherwise it returns True representing a successful submission. Since validators are designed to pass error information in through theNode.msgs
attribute then this error information is in turn availible through the rendering context.Parameters: data (dictionary) – The data to be passed through the Form._processor. If the data is in the form of a dictionary where the key is the ‘name’ of the form field and the data is a string then no post-processing is neccessary. Returns: Whether the validators are blocking submission and a re-render of the form with the validation data passed in.