corvid
corvid

Reputation: 11177

post request with a form, then being able to save the data it returned on a separate request

let's say I have a form like this

<form role="form">
  <div class="form-group">
    <label for="exampleInputEmail1">Email address</label>
    <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email">
  </div>
  <div class="form-group">
    <label for="exampleInputPassword1">Password</label>
    <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
  </div>
</form>

then I have a fairly generic route that sorts through a static file

@app.route('/search/properties/', methods=['GET', 'POST'])
def properties_search():
  form = request.form
  if request.method == 'POST':
    properties = CSVReader('properties.csv')
    d = [x._asdict() for x in properties.data]
    gen = stream_with_context(search_csv(d, form))
    return Response(stream_with_context(
      stream_template('advanced_search/results.html', form=form, rows=gen)
    ))
  return render_template('advanced_search/advanced.html', form=form)

This will render a page with the results that I am looking for -- and all is well with that. However, on the page, I also want to add the capability to download the data as a CSV.

| download csv | | return to search |
- result 1
- result 2
...

The problem I am having is that, after the post request, I need some way to know what their last form request was on the application.

What is the best way to do this?

Upvotes: 1

Views: 149

Answers (1)

Stephen Byrne
Stephen Byrne

Reputation: 7475

There isn't a single "best way" to do this, because what's "best" in your specific case might not suit another scenario. That being said, below are a couple of possible ways (which barely scratch the surface of what you need to do with them to make them production-ready) and there are many, many more.

You really need to research the options yourself here; the scenario you have is not specific to python or flask, it's pretty much a problem for anyone building an HTTP application that needs to remember state!

Render the received form values in hidden inputs

  • When you are rendering the initial search results. Render the "Download CSV" as the submit input of a form that will post these values back but to the download renderer.

    I wouldn't be a huge fan of this as it requires a button and a POST when you are really looking for a GET and if you actually need the password in order to render the results, it's a security problem.

Whilst rendering the initial search results page, render the link for "Download CSV" such that it contains some kind of session ID.

  • When you receive the initial POST of the form to generate the search results. Take the email and store it in a database table (or some other persistent storage mechanism), recording some kind of generated ID as a result of this operation. You could also save any other search parameters the user submitted, etc.

  • This ID then becomes a querystring parameter of the link to "Download as CSV". i.e. when your template renders the initial search page, the link ends up like "http://blahblah.com/download?ID=12345"

  • When the link is requested by the user, lookup the search/ user information from the database based on the querystring ID parameter, and then pass it to whatever template will render the CSV for you.

  • There are many flavours of this approach and you need to pick the best for your scenario - you can save the search criteria for replay, or save the actual search results, it depends on the nature of the search, how expensive it is to run and whether "download as CSV" has to replay the search OR return the exact results originally obtained, etc), and you will also need to harden it. Don't send raw database IDs - send a hashed/encrypted version of them so that users cannot "guess" download IDs, etc.

I'd recommend this kind of approach because it doesn't require you to return the username/password to the client at all.

Hopefully that will get you thinking :)

Upvotes: 1

Related Questions