Tag Archives: Flask form submission

The new submission form

This post will detail the processing of a new submission form that contains a multiple select form element.

A widget is described as a set of versioned components where a component version could be used in many widgets:
components:
id: INT
name: STRING
description: TEXT

component_versions:
id: INT
name: STRING
component_id: INT

widgets:
id: INT
name: STRING
description: TEXT

component_version_widgets:
component_id: IN
widget_id: INT

A simple route to handle a request to create  new item could be something like:

@app.route('/widgets/new', methods=['GET', 'POST'])
def newWidget():
    # Wasn't able to find any real documentation or examples of how to use the concat
    # function but sort of figured it out by looking at the PeeWee source code. This
    # uses a local function to create a list of list of lists of the items to be
    # displayed in the multiple select element in the form
    # [ [1, <component_name>-<component_version> ], [2, <component_name>-<component_version> ], ... ]
    # that is hopefully easier to process in the template 
    cvids = dict2lol(component_versions
            .select(component_versions.id.alias("cmpntid"),
             components.name.concat("-").concat(component_versions.name).alias("cmpntname"))
             .join(components, JOIN.INNER,
             on=component_versions.component_id == components.id)
             .dicts(), 'cmpntid', 'cmpntname')
    if request.method == 'POST':
        (status, msg) = validateWidget(request.form)
         # Use .getlist() to retrieve the multiple select items
         slctd = request.form.getlist('component_version_ids')
         if status:
             # Although auto-increment works with SQLite3 it didn't seem to work
             # when creating records with PeeWee
             nextId = widgets.select(fn.Max(widgets.id)+1).scalar()
             widgets.create(id=nextId,
                                         name=request.form['name'], \
                                         description=request.form['description'], \
                                         updated_by="config.admin", \
                                          updated_at=datetime.datetime.now())
             for cvid in slctd:
                  component_version_widget.create(widget_id=nextId, component_version_id=cvid)
                  logActivity('create', 'component_version_widgets', nextId, ("component_version_id: %s" % pvid))

             logActivity('create', 'widgets', nextId, request.form['name'])
              flash(("Saved new widget, update for %s." % request.form['name']))
              return redirect('/widgets')
        else:
             flash("Widget creation failed: %s." % msg)
              slctd = []
              for pv in request.form.getlist('component_version_ids'):
                 slctd.append(int(pv))
                 wdgt = {"description": request.form['description'],
                           "name": request.form['name'],
                           "component_version_ids": slctd}
    else:
        wdgt = {'name': 'Widget name', 'description': 'Brief description', "component_version_ids": []}
    return render_template('views/widgets/new.html', wdgt=wdgt, cvids=cvids, req=request)

Notes:

  • cvids: a list of lists of select options in the form [id, name]. Possibly being
    lazy but this is a bit simpler than trying to process what might come out of a PeeWee
    result set
  • slctd: a list of selected options from the select form; either from the database or the
    submitted form data. When creating this list from the submitted form we need to cast the id values as int’s or they won’t be recognised by the template inline conditional.
  • wdgt: dict containing the values for the template form items
  • formatting python code in the edit window is quite tricky so no guarantees that it compiles cleanly

The template to display this might look a bit like,

<form name="widgets" method="POST" action="{{ req.path }}">

<p>
Widget Widget name Description {{ rel['description'] }}
Component versions:
{%- for opt in cvids-%} {{ opt[1] }} {%- endfor -%}
</p>
<input type="submit" name="submit" value="Save widget"> </form>

The only real point to note in here is the method by which the ‘selected’ attribute is added to the option in the select list: the inline if.

References

The edit form submission

This post will detail the processing of an edit submission form that contains a multiple select form element.

A widget is described as a set of versioned components where a component version could be used in many widgets:

components:
id: INT
name: STRING
description: TEXT

component_versions:
id: INT
name: STRING
component_id: INT

widgets:
id: INT
name: STRING
description: TEXT

component_version_widgets:
component_id: INT
widget_id: INT

@app.route('/widgets/<int:id>/edit', methods=['GET', 'POST'])
def editWidget(id):
    # Get the component names and versions and format them for easy display
    # in the template
    cvids = dict2lol(component_versions
                .select(component_versions.id.alias("cmpntverid"),
                 components.name.concat("-").concat(component_versions.name).alias("cmpntname"))
                 .join(components, JOIN.INNER,
                 on=component_versions.component_id == components.id)
                 .dicts(), 'cmpntverid', 'cmpntname')

    if request.method == 'POST':
         (status, msg) = validateWidget(request.form)
         slctd = request.form.getlist('component_version_ids')
         if status:
             savewdgt = widgets(id=id,
                       name=request.form['name'], \
                       description=request.form['description'], \
                       updated_by="config.admin", \
                       updated_at=datetime.datetime.now())
             savewdgt.save()
            # Delete the current component versions for this widget and save
             # the values submitted in the form
             qry = component_version_widgets.delete().where(component_version_widgets.widget_id == id)
             qry.execute()
             for cvid in slctd:
                 component_version_widgets.create(widget_id=id, component_version_id=cvid)
 logActivity('update', 'component_version_widgets', id, ("component_version_id: %s" % cvid))

                logActivity('update', 'widgets', id, request.form['name'])
            flash(("Saved update for widget, %s." % request.form['name']))
            return redirect('/widgets')
        else:
             flash(("widget update failed: %s." % msg))
             slctd = []
             for cv in request.form.getlist('component_version_ids'):
                slctd.append(int(cv))
                wdgt = {"description": request.form['description'], "name": request.form['name'],
 "component_version_ids": slctd}
    else:
        # Display the initial edit form with details from the database
         # Get the details of the item to be edited 
         try:
             rs = widgets.select().where(widgets.id == id).get()
         except DoesNotExist:
             flash(("Cannot locate widget record with id = %s" % id))
         return redirect('/widgets')


    slctd = []
    for cvid in component_version_widgets \
           .select(component_version_widgets.component_version_id) \
           .where(component_version_widgets.widget_id == id):
        slctd.append(cvid.component_version_id)
        wdgt = {'name': rs.name, 'description': rs.description, 'component_version_ids': slctd}

    return render_template('views/widgets/edit.html', wdgt=wdgt, cvids=cvids, req=request)

Notes:

  • cvids: a list of lists of select options in the form [id, name]. Possibly being lazy but this is a bit simpler than trying to process what might come out of a PeeWee result set
  • slctd: a list of selected options from the select form; either from the database or the
    submitted form data. When creating this list from the submitted form we need to cast the id values as int’s or they won’t be recognised by the template inline conditional.
  • wdgt: dict containing the values for the template form items

The dict2lol function is simply:

def dict2lol(rs, val, text):
    lol = []
    for row in rs:
        lol.append([row[val], row[text]])
    return(lol)

The template to display this might look a bit like,

<form name="widgets" method="POST" action="{{ req.path }}">
<p>
Widget Widget name Description {{ rel['description'] }}
Component versions:
{%- for opt in cvids-%} {{ opt[1] }} {%- endfor -%}
</p>
<input type="submit" name="submit" value="Save widget"> </form>

The only real point to note in here is the method by which the ‘selected’
attribute is added to the option in the select list: the inline if.

References