9

What is the best way to edit a pandas dataframe in flask?

I have a dataframe which I want to output on a HTML page with flask (there are many examples how to do this). However, I don't just want to output it, but I want to make it editable. Ideally each field should be a html input field.

I would like to avoid to have to create a form manually and then reconvert it to a dataframe. Is there any elegant solution to that? Does pandas or any other package offer any functionality that could simplify that task?

Julien Marrec
  • 11,605
  • 4
  • 46
  • 63
Nickpick
  • 6,163
  • 16
  • 65
  • 116
  • Well, at that point, you're making a web app...so, you'll need JavaScript for changing the parts of the table to editable fields upon clicking, AJAX (or web sockets, if you wanna be fancy) to update the field when the user's done editing, etc, etc, etc. –  Jan 04 '17 at 19:11
  • No need for JavaScript. I'm happy if there a way to generate a form or a model somehow and then send it back to the server with a submit button, which will reconvert it to a dataframe – Nickpick Jan 04 '17 at 19:13

1 Answers1

14

You can make use of df.style (a Styler instance) to render a DataFrame as a grid of HTML inputs.

np.random.seed(0)

df = pd.DataFrame(np.random.randint(0, 100, (3, 3)))
df.style.format('<input name="df" value="{}" />').render()

This will render as

HTML table

If you wrap the result in a <form> and submit it to some endpoint, the request query string (or POST body) will look like

df=44&df=47&df=64&df=67&df=67&df=9&df=83&df=21&df=36 

Note that these are the cells of the data frame in row-major order. At this point, you can re-create the data frame using

df = pd.DataFrame(np.asarray(request.values.getlist('df'), dtype=np.int).reshape((3, 3)))

As you suggested in your comment, another approach is to name HTML inputs with the column name, to avoid having to reshape the data.

def html_input(c):
    return '<input name="{}" value="{{}}" />'.format(c)

df.style.format({c: html_input(c) for c in df.columns}).render()

The data sent to the server will then look like

0=44&1=47&2=64&0=67&1=67&2=9&0=83&1=21&2=36

and you can restore the data frame using

df = pd.DataFrame(request.values.lists())

This is more elegant than the above, apart from the need to create the formatter dictionary {c: html_input(c) for c in df.columns}. Unfortunately, the formatter function is only passed the value, and none of the index information.

Igor Raush
  • 15,080
  • 1
  • 34
  • 55
  • That's great. Only thing improvement I could see, is there a way to keep the names of the dataframe columns, so I can read them back directly rather have to do a .reshape(...). It's rather unpractical to have all fields named as df, as I have lots of them. – Nickpick Jan 05 '17 at 15:05
  • @nick, see my edit. I also slightly simplified the first approach to pass a string to `df.style.format` which is a shortcut for what I was previously doing. – Igor Raush Jan 05 '17 at 20:27
  • Hi @IgorRaush , could you give some details as to how to wrap the table result in a form in order to get the string "df=44&df=47&df=64&df=67&df=67&df=9&df=83&df=21&df=36"? I'm following your first method and have the table rendered on the html. After modifying the fields, I cannot send these new inputs back to the server using POST. I hope you could help me with this final request as it's the very last step that I cannot get through – Alice_inwonderland Dec 20 '20 at 22:26