Nodes

Nodes drive the actual rendering of your Form. Internally a Form keeps track of a list of Node‘s and then passes them off to the Renderer when a render of the Form is requested. Lets look at a simple example Form as shown in the introduction:

from yota import Form
from yota.nodes import *

class PersonalForm(Form):

    first = EntryNode()
    last = EntryNode()
    address = EntryNode()
    submit = SubmitNode(title="Submit")

All of the attributes defined in the above class are Node instances. Internally there is some trickery that preservers the order of these attributes, but this is not important to understand for using them. Just realize that unlike a regular object in Python, the order of these attributes effects the output of your Form.

Note

Some attribute names are reserved and trying to overwrite them with Node attributes will break things. Ensure that the names you select for your Node attributes do not collide with parameters to Form or keyword attributes that you pass to your Form.

The canonical Node is just a reference to some kind of rendering template (by default, Jinaj2 templates) and some associated metadata that will control how the template is rendered.

A Simple Node

Let’s examine one of the builtin Nodes availible in Yota, and some of the things we can do with it. Let us look at the nodes.EntryNode. It has the following template:

{% extends base %}
{% block control %}
<input data-piecewise="{{ piecewise_trigger }}"
       type="text"
       id="{{ id }}"
       value="{{ data }}"
       name="{{ name }}"
       placeholder="{{ placeholder }}">
{% endblock %}

Above we see what looks vaugly like HTML. If you’re not familiar with Jinja it would be a good idea to give their documentation a cursory glance before proceeding much further. However, the jist is that the sections enclosed in double curly-braces {{ }} will be replaced with variables, while the {% %} enclosed areas represent some sort of control structure. The meat of the above template is the input field. You can see that most of its attributes are replaced by variables.

Now take a look at the extends portion on the first line of our template. This is actually importing another template which is used as the base for many different builtin Nodes in Yota. We can see that template here:

<div class="control-group">
    {% block error %}
        {% if errors %}
        <div class="alert alert-error">
            {{ errors[0]['message'] }}
        </div>
        {% endif %}
    {% endblock %}
    {% if label %}
        {% block label %}
            <label class="control-label" for="{{ name }}">{{ title }}</label>
        {% endblock %}
    {% endif %}
    <div class="controls">
        {% block control %}
        {% endblock %}
    </div>
</div>

This template is just the default horizontal form layout for Bootstrap. Up top you can see a section reserved for displaying errors and in the middle a section to display a label. At the bottom is where the other template gets injected through the magic of blocks. Again, refer to the Jinja2 documentation for more information on this.

The actual Node definition is basically nothing:

class EntryNode(BaseNode):
    template = 'entry'

Notice that the template is just entry, not entry.html. This is because the renderer auto-appends the suffix so Nodes can be used across different templating engines.

To understand more of what’s going on under the covers, here’s some explaination about how the variables used in the above templates are generated.

Identifiers

Some of the values, such as id and name will get automatically generated by Node.set_identifiers(), and will be based off of what you name the attribute in you class definition.

Data

The data attribute is automatically populated when validation is run. This is performed by Node.resolve_data() and talked about in the Custom Node section below.

Errors

This attriubte is a list of errors generated by validator callables. More about this in validation.

Other

The remained of variables in the above template are just plain old attributes with defaults. Keep in mind that attriubtes/arguments in Yota do not behave quite like they do normally in Python. Learn more about this in Attribute and Argument behaviour in the Form page.

The majority of Node attributes may be overridden either through initialization of the function, like so:

my_node = EntryNode(name="Something else", template="custom_entry")

Or by setting it as a class attribute in your Node definition like so:

class EntryNode(BaseNode):
    template = 'entry'
    _ignores = ['template']

However, keep in mind that attributes that are auto-generated, such as name, id, and title should not be set as class attributes since they will get overriden when they are generated. By default, the following attributes are reserved:

  • name
  • id
  • title
  • errors
  • data
  • _attr_name
  • _ignores
  • _requires
  • _create_counter

Custom Nodes

Most Node definitions are quite simple, with the majority simply changing the template being used. More complex Node semantics are availible by overriding some of their built in methods, such as Node.resolve_data() or Node.set_identifiers(). These are all described in the API documentation, but some examples will be given here of how you might wish to use these methods.

Changing data resolution

The default Node implementation assumes that your Node only contains one input, and as such its data output is assumed to be tied directly to this single input. The Node.set_identifiers() method defines a defualt implementation for naming your input field that looks something like this:

try:
    self.data = data[self.name]
except KeyError:
    self.data = self._null_val

You can see above that the Node’s name is used to pick out the data that is associated with this Node. But say your Node includes multiple input fields, perhaps you have a date picker. A simple template may look like this:

Month: <input type="text" name="{ name }_month" placeholder="Month" /><br />
Day: <input type="text" name="{ name }_day" placeholder="Day" /><br />
Year: <input type="text" name="{ name }_year" placeholder="Year" /><br />

Now of course the Node.resolve_data() will fail to find anything associated with “name” since it doesn’t exist, and instead an implementation may look something like this.

def resolve_data(self, data):
    try:
        day = data[self.name + '_day']
        month = data[self.name + '_month']
        year = data[self.name + '_year']
    except KeyError:
        self.data = self._null_val

    # set data to a tuple of values for validation
    self.data = (year, month, day)

Aside from our crappy looking form, and some lack of bounds checking everything is good. Now say we wanted to make this form work with AJAX, and we wanted to make the border of each of the form elements red when there was an error. Well this is a problem, because our JavaScript doesn’t implicity know how to find the elements. You could modify your render_error method to manually catch this case, but this wouldn’t be a very resiliant option. Instead, we can make our default functions aware of these extra elements. This is done through the json_identifiers method.

Modifying AJAX rendering

Node.json_identifiers() is executed by validation methods when sending erros back to the client side via JSON. It is used to give the client side inoformation about where the error data should be placed in the DOM. Essentially your render_error and render_success methods are passed an ‘ids’ object, and this is a direct serialization of the return from this function. The default render_error and render_success methods expect the following keys:

  • ‘error_id’: This should be an id value of a DOM element that you would like to place your error ‘message’ in. This is not actually used by default, but is implemented by all builtin Nodes. It corresponds to the DOM element that renders regular errors.
  • ‘elements’: This supplies a list of all ids of form elements in the Node. Error tooltips point to the first element.

set_identifiers

When the Node is added to a Form the set_identifiers method is called to setup some unique names to be used in the template and possibly AJAX. Perhaps you’d like a different semantic for automatically titling your date pickers? Overriding this function may also be wanted if you’re writing a Node with multiple form elements in it. This all depends on your preference.

def set_identifiers(self, parent_name):
    super(MySuperSpecialNode, self).set_identifiers(parent_name)
    if not hasattr(self, 'title'):
        self.title = self._attr_name.capitalize() + " Very Special"

Builtin Nodes

class yota.nodes.BaseNode(**kwargs)[source]

This base Node supplies the name of the base rendering template that is used for standard form elements. This base template provides error divs and the horizontal form layout for Bootstrap by default through the horiz.html base template.

class yota.nodes.NonDataNode(**kwargs)[source]

A base to inherit from for Nodes that aren’t designed to generate output, such as the SubmitNode or the LeaderNode. It must override resolve_data, otherwise the data will be set to Node._null_val.

class yota.nodes.ListNode(**kwargs)[source]
Node for providing a basic drop down list. Requires an attribute that

is a list of tuples providing the key and value for the dropdown list items.

Note

The first item of the tuple must be a string in order to match returned data properly and re-select the same list item when a validation error occurs.

Attr items:Must be a list of tuples where the first element is the value of the second is the label.
class yota.nodes.RadioNode(**kwargs)[source]

Node for providing a group of radio buttons. Requires buttons attribute.

Attr buttons:Must be a list of tuples where the first element is the value of the second is the label.
class yota.nodes.CheckGroupNode(**kwargs)[source]

Node for providing a group of checkboxes. Requires boxes attribute. Instead of defining an ID value explicitly the Node.set_identifiers defines a prefix value to be prefixed to all id elements for checkboxes in the group. The output data is a list containing the names of the checkboxes that were checked.

Attr boxes:Must be a list of tuples where the first element is the name, the second is the label.
class yota.nodes.ButtonNode(**kwargs)[source]

Creates a button in your form that submits no data.

class yota.nodes.EntryNode(**kwargs)[source]

Creates an input box for your form.

class yota.nodes.PasswordNode(**kwargs)[source]

Creates an input box for your form.

class yota.nodes.FileNode(**kwargs)[source]

Creates an input box for your form.

class yota.nodes.TextareaNode(**kwargs)[source]

A node with a basic textarea template with defaults provided.

Attr rows:The number of rows to make the textarea
Attr columns:The number of columns to make the textarea
class yota.nodes.SubmitNode(**kwargs)[source]

Displays a submit button on the right side to align with Form elements

class yota.nodes.LeaderNode(**kwargs)[source]

A Node that does few special things to setup and close the form. Intended for use in the start and end Nodes.

Node API

class yota.Node(**kwargs)

Nodes are holders of context for rendering and displaying validating for a portion of your Form. This default base Node is designed to provide a template along with specific context information to a templating engine such as Jinja2. For validation a Node acts as an information source or an error sink. Essentially Nodes can be used to source data for use in a Check, and they can then be delivered some sort of validation error via a the internal errors attribute.

Note

By default all keyword attributes passed to a Node’s init function are passed onto the rendering context. To override this, use the Node._ignores attribute.

Parameters:
  • _attr_name (string) – This is how the Node is identified in the Form. If populated automatically if the Node is defined in an a Form class definition, however if the Node is added dynamically it will need to be defined before adding it to the Form.
  • _ignores (list) – A List of attribute names to explicity not include in the rendering context. Mostly a niceity for keeping the rendering context clutter free.
  • _requires (list) – A List of attributes that will be required at render time. An exception will be thrown if these attributes are not present. Useful for things like lists that require certain data to render properly.
  • template (string) – String name of the template to be parsed upon rendering. This is passed into the Form._renderer so it needs to be whatever that is designed to accept. Jinja2 is looking for a filename like ‘node’ that occurs in it’s search path.
  • validators – An optional attribute that specifies a Check object, or list of Check objects to be associated with the Node. This is automatically at render time.
  • _null_val – When form submission data is passed in for validation and the Node.resolve_data() method cannot identify anything, the data attribute will be set to this value. Defaults to “”.

The default Node init method accepts any keyword arguments and adds them to the Node’s rendering context. In addition any class attributes may be added to custom Nodes and these attributes will be copied at instantiation time and passed into the rendering context.

_attr_name = None
_create_counter = 0

Allows tracking the order of Node creation

_ignores = ['template', 'validator']
_null_val = ''
_requires = []
add_error(error)

This method serves mostly as a wrapper alowing for different error ordering semantics, or possibly error post-processing. Errors from validation methods should be added in this way allowing them to be caught. More information about what gets passed in in the Validators and Checks section.

data = ''
errors = []
get_context(g_context)

Builds our rendering context for the Node at render time. By default all attributes of the Node are added to the global namespace and the global rendering context is passed in under the variable ‘g’. This function is designed to be overridden for customization. :param g_context: The global rendering context passed in from the rendering method.

Parameters:g_context – This is the global context passed in from the parent Form object. By default it’s included under the ‘g’ key, similar to Flask’s globals.
get_list_names()

As the title suggests this needs to return an iterable of names. These should be names corresponding to form elements that the Node will generate. This list is uesed by piecewise validation to determine if a Node has been visisted based on a list of names that have been visited, bridging Nodes to elements.

json_identifiers()

Allows passing arbitrary identification information to your JSON error rendering callback. For instance, a common use case is the display an error message in a pre-defined div with a specific id. Well you may perhaps pass in an ‘error_div_id’ attribute to the JSON callback to use when trying to render this error. The default for Yota builtin nodes is to pass ‘error_id’ indicating the id of the error container in addition to a list containing all input elements in the Node’s ids.

label = True
piecewise_trigger = 'blur'
resolve_data(data)

This method links data from form submission back to Nodes. HTML form data is represented by a dictionary that is keyed by the ‘name’ attribute of the form element. Since most Nodes only render a single form element, and the default set_identifiers generates a single ‘name’ attribute for the Node then this function attempts to find data by linking the two together. However, if you were to change that semantic this would need to change. Look at the CheckGroupNode for a reference impplementation of this behaviour, or the Docs under “Custom Nodes”. This method should operate by setting its own data attribute, as this is how Validators conventionally look for data.

... note:: This method will throw an exception at validation time if
the data dictionary contains no key name, so it important to override this function to a NoOp if your Node generates no data. NonDataNode was created for this exact purpose.
Parameters:data – The dictionary of data that is passed to your validation method call.
set_identifiers(parent_name)

This function gets called by the parent Form when it is initialized or inserted. It is designed to set various unique identifiers. By default it generates an id for the Node that is {parent_name}_{_attr_id}, a title for the Node that is the _attr_name capitalized, and a name for the element that is just the _attr_name. All of these attributes are then passed onto the rendering context of the Node by default. By default all of these attributes will yield to attributes passed into the __init__ method.

Parameters:parent_name (string) – The name of the parent form. Useful in ensuring unique identifiers on your element names.
template = None
validators = []

Project Versions

Table Of Contents

Previous topic

Using Forms

Next topic

Validators and Checks

This Page