Search code examples
pythonflaskflask-wtformswerkzeug

Passing more than just the resultant page out of a Flask POST


(Flask novice alert)

Given the following to upload and save a file in Flask:

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        file = request.files['file']
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            return render_template_string('''
                {% extends "base.html" %}
                {% block content %}
                <h4>File uploaded</h4>
                <p><a href={{ url_for('members_page') }}>Back</a></p>
                {% endblock %}
                ''')
        elif not allowed_file(file.filename):
            return render_template_string('''
            {% extends "base.html" %}
            {% block content %}
            <h3>Please try again</h3>
            <h4>File must be a .csv</h4>
            <p><a href={{ url_for('upload_file') }}>Back</a></p>
            {% endblock %}
            ''')
    return render_template_string('''
    {% extends "base.html" %}
    {% block content %}
    <h4>Upload CSV of Company/URL data</h2>
                <form action="" method="post" enctype="multipart/form-data">
                    <input type="file" name="file" />
                    <input type="submit" />
                </form>
    {% endblock %}
    ''')

I wish to make filename available within another function:

@app.route('/scrape', methods=['GET', 'POST'])
@login_required                                 # Use of @login_required decorator
def scrape():
    parser = ConfigParser()
    parser.read('config.ini')
    keywords = parser.get('scrape', 'keywords').replace(' ', '').split(',')
    jobs = scraper.scrape(os.path.join(app.config['UPLOAD_FOLDER'], filename), keywords)

The above is the desired intent, where filename is known by the scrape fucnction. Obviously that is not yet the case. With upload_file() already having a return value in the positive case (a confirmation page), how can I make filename available? UPLOAD_FOLDER will contain more than just the uploaded file, so I can't just join this path with whatever is in there.

Where this a non-Flask program, I would probably return locals() and then access the appropriate key, but I imagine that's not possible here if I want to maintain the serving up of the confirmation page.


Solution

  • You need to somehow connect two requests. If many users request the first one, a then someone requests a /scrape, how do you know which one is requesting, and which filename does he belong to?

    1. You can use a session (a cookie session for example, see http://pythonhosted.org/Flask-Session/) to keep track of the uploaded file. Store the filename in the session, and the when the same user (with the same cookie) requests /scrape, you can retrieve the filename from the user session.
    2. You can include the filename to use in the second request. This way, the user himself has to keep track of the files that he uploaded.

    In either case, but especially in the latter, it's important to think about ownership of files: which user has access to which file on your system?