PhotoDateChanger
Easily change the dates and times on your photos and videos
Click here for more info about the app
VocabReminder
English dictionary with notifications so you won't forget what you're studying!
Click here for more info about the app
VocabQuiz
The app that quizzes and scores you on your vocabulary!
Click here for more info about the app

Reshape an Array of Form Inputs for Flask with getlist()

       

This is how to reshape an array using Python without Numpy.

This is a fairly simple task but it can be a little confusing to wrap your head around. I ran into this problem while working with Flask, working with array from form fields.

I wanted to use the name attribute as an array In Rails you can use the name[] syntax to create an array forms.

This is useful for dynamically adding form fields to a page.

<input type="text" name="todo[]"/>
<input type="text" name="todo[]"/>
... more ...

On the server we would expect to see todo: ["item A", "item B"] Very convienent.

However, in flask the behavior is a little different. I've seen a few packages that try to solve the problem. But I haven't seen one that compares to Rails. However, the solution is short enough to implement yourself.

Here is the general approach.

headers = ['a', 'b', 'c']

values = [
    ['a0', 'a1'],
    ['b0', 'b1'],
    ['c0', 'c1'],
]

items = [{} for i in range(len(values[0]))]
for x,i in enumerate(values):
    for _x,_i in enumerate(i):
        items[_x][headers[x]] =_i

items == [
    {'a': 'a0', 'b': 'b0', 'c': 'c0'},
    {'a': 'a1', 'b': 'b1', 'c': 'c1'},
]

To use this in Flask we will need to structure our form like this

<form>
    <input name="name[]">
    <input name="price[]">
    <input name="quantity[]">
</form>

I've left out non essential attributes for the example.

And on the server we will use request.form.getlist() to get at the array. Here is where the reshaping code comes into play.

from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route('/save', methods=["POST"])
def save_path():
    headers = ('name', 'price', 'quantity')
    values = (
        request.form.getlist('name[]'),
        request.form.getlist('price[]'),
        request.form.getlist('quantity[]'),         
    )
    items = [{} for i in range(len(values[0]))]
    for x,i in enumerate(values):
        for _x,_i in enumerate(i):
            items[_x][headers[x]] = _i
    return jsonify(items)

The order of your headers and the order in which you set your values matter because we're using the index of the arrays (with enumerate).

To take this one step further, you could move the logic into a method for reusability and a cleaner API.

def get_list(headers):
    values = [request.form.getlist(h) for h in headers]
    items = [{} for i in range(len(values[0]))]
    for x,i in enumerate(values):
        for _x,_i in enumerate(i):
            items[_x][headers[x]] = _i
    return items

    @app.route("/save", methods=["POST"])
    def save_path():
        return jsonify(get_list(('label', 'price', 'quantity')))
# [
#   {
#     "label": "a", 
#     "price": "a", 
#     "quantity": "a"
#   }, 
#   {
#     "label": "b", 
#     "price": "b", 
#     "quantity": "b"
#   }
# ]
Tagged w/ #python #flask #web development #params