Skip to main content

Flask: Handling GET and POST Requests

Flask: Handling GET and POST Requests

Flask, a lightweight Python web framework, provides robust support for handling HTTP requests, including GET and POST methods, which are essential for building dynamic web applications. These methods enable interaction between clients (e.g., browsers) and servers, making them critical for data-driven applications like dashboards or machine learning (ML) interfaces using Pandas. This guide covers Flask handling GET and POST requests, including setup, processing, integration with Jinja2 templates, best practices, and practical examples, with a focus on data-driven use cases.


01. Overview of GET and POST Requests

HTTP requests are used to communicate between clients and servers. Flask routes handle these requests to perform actions like retrieving data (GET) or submitting data (POST).

  • GET Request: Retrieves data from the server (e.g., loading a page or fetching query parameters).
  • POST Request: Sends data to the server for processing (e.g., form submissions).
  • Key Components: request object, route decorators, and form handling.
  • Use Cases: Displaying Pandas DataFrames, submitting ML model inputs, or filtering dashboard data.

1.1 Flask’s request Object

The request object, imported from flask, provides access to request data:

  • request.method: The HTTP method ('GET' or 'POST').
  • request.args: Query parameters for GET requests.
  • request.form: Form data for POST requests.

02. Handling GET Requests

GET requests are used to retrieve data, often via URL query parameters or direct page access. Flask routes handle GET requests by default.

2.1 Basic GET Request

Example: Simple GET Request

File: app.py

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def home():
    return render_template('index.html')

if __name__ == '__main__':
    app.run(debug=True)

File: templates/index.html

<!DOCTYPE html>
<html>
<head>
    <title>Home</title>
</head>
<body>
    <h1>Welcome to the Flask App</h1>
    <p>This is a GET request.</p>
</body>
</html>

Output (http://127.0.0.1:5000/):

A simple page with the welcome message.

Explanation:

  • @app.route('/'): Handles GET requests to the root URL by default.
  • render_template: Renders the Jinja2 template.

2.2 GET with Query Parameters

Query parameters (e.g., ?name=Alice) are accessed via request.args.

Example: GET with Query Parameters

File: app.py

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/greet')
def greet():
    name = request.args.get('name', 'Guest')
    return render_template('greet.html', name=name)

if __name__ == '__main__':
    app.run(debug=True)

File: templates/greet.html

<!DOCTYPE html>
<html>
<head>
    <title>Greet</title>
</head>
<body>
    <h1>Hello, {{ name | title }}!</h1>
    <p>This is a GET request with query parameters.</p>
</body>
</html>

Output (http://127.0.0.1:5000/greet?name=Alice):

A page displaying Hello, Alice!.

Output (http://127.0.0.1:5000/greet):

A page displaying Hello, Guest!.

Explanation:

  • request.args.get('name', 'Guest'): Retrieves the name parameter, defaulting to Guest.
  • | title: Formats the name in the template.

03. Handling POST Requests

POST requests are used to submit data, typically via HTML forms. Flask routes must explicitly allow POST methods using the methods parameter in @app.route.

3.1 Basic POST Request

Example: Simple Form Submission

File: app.py

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/submit', methods=['GET', 'POST'])
def submit():
    if request.method == 'POST':
        name = request.form.get('name', 'Guest')
        return render_template('result.html', name=name)
    return render_template('form.html')

if __name__ == '__main__':
    app.run(debug=True)

File: templates/form.html

<!DOCTYPE html>
<html>
<head>
    <title>Form</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <h1>Submit Your Name</h1>
        <form method="post">
            <div class="form-group">
                <label>Name:</label>
                <input type="text" name="name" class="form-control">
            </div>
            <button type="submit" class="btn btn-primary">Submit</button>
        </form>
    </div>
</body>
</html>

File: templates/result.html

<!DOCTYPE html>
<html>
<head>
    <title>Result</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <h1>Hello, {{ name | title }}!</h1>
        <a href="{{ url_for('submit') }}" class="btn btn-secondary">Back</a>
    </div>
</body>
</html>

Output (/submit):

  • GET: Displays a Bootstrap-styled form.
  • POST (name=Alice): Renders a page with Hello, Alice! and a back button.

Explanation:

  • methods=['GET', 'POST']: Allows both GET and POST requests.
  • request.form.get('name', 'Guest'): Retrieves form data, with a default.
  • form method="post": Submits data to the same route.

3.2 Handling File Uploads with POST

POST requests can handle file uploads, useful for ML model inputs or data files.

Example: File Upload

File: app.py

from flask import Flask, render_template, request
import os

app = Flask(__name__)
UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        if 'file' not in request.files:
            return render_template('upload.html', error='No file selected')
        file = request.files['file']
        if file.filename == '':
            return render_template('upload.html', error='No file selected')
        file.save(os.path.join(app.config['UPLOAD_FOLDER'], file.filename))
        return render_template('upload.html', message=f'File {file.filename} uploaded')
    return render_template('upload.html')

if __name__ == '__main__':
    os.makedirs(UPLOAD_FOLDER, exist_ok=True)
    app.run(debug=True)

File: templates/upload.html

<!DOCTYPE html>
<html>
<head>
    <title>Upload</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <h1>Upload File</h1>
        {% if error %}
            <p class="text-danger">{{ error }}</p>
        {% endif %}
        {% if message %}
            <p class="text-success">{{ message }}</p>
        {% endif %}
        <form method="post" enctype="multipart/form-data">
            <div class="form-group">
                <label>File:</label>
                <input type="file" name="file" class="form-control">
            </div>
            <button type="submit" class="btn btn-primary">Upload</button>
        </form>
    </div>
</body>
</html>

Output (/upload):

  • GET: Displays a file upload form.
  • POST (valid file): Saves the file to uploads/ and shows a success message.
  • POST (no file): Shows an error message.

Explanation:

  • enctype="multipart/form-data": Required for file uploads.
  • request.files['file']: Accesses the uploaded file.
  • file.save: Saves the file to the specified folder.

04. GET and POST in Data-Driven Applications

GET and POST requests are critical for data-driven Flask applications, enabling data retrieval (e.g., dashboard displays) and submission (e.g., ML model inputs).

Example: ML Prediction Dashboard

File: app.py

from flask import Flask, render_template, request
import pandas as pd

app = Flask(__name__)

@app.template_filter('probability')
def probability_filter(value):
    try:
        return f"{float(value) * 100:.1f}%"
    except (ValueError, TypeError):
        return value

@app.route('/')
def home():
    return render_template('home.html')

@app.route('/dashboard', methods=['GET', 'POST'])
def dashboard():
    df = pd.DataFrame({
        'Name': ['Alice', 'Bob', 'Charlie'],
        'Age': [25, 30, 35],
        'Salary': [50000, 60000, 55000]
    })
    prediction = None
    if request.method == 'POST':
        prediction = float(request.form.get('prediction', 0.5))
    return render_template('dashboard.html', data=df.to_dict(orient='records'), prediction=prediction)

if __name__ == '__main__':
    app.run(debug=True)

File: templates/base.html

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}ML App{% endblock %}</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>
    <nav class="navbar navbar-light bg-light">
        <a class="navbar-brand" href="{{ url_for('home') }}">ML App</a>
    </nav>
    <div class="container mt-3">
        {% block content %}{% endblock %}
    </div>
</body>
</html>

File: templates/home.html

{% extends 'base.html' %}

{% block title %}Home{% endblock %}

{% block content %}
    <h1>Welcome</h1>
    <p>Visit the <a href="{{ url_for('dashboard') }}">Dashboard</a> to view data and make predictions.</p>
{% endblock %}

File: templates/dashboard.html

{% extends 'base.html' %}

{% block title %}Dashboard{% endblock %}

{% block content %}
    <h1>ML Prediction Dashboard</h1>
    <table class="table table-striped">
        <thead>
            <tr>
                <th>Name</th>
                <th>Age</th>
                <th>Salary</th>
            </tr>
        </thead>
        <tbody>
            {% for row in data %}
                <tr>
                    <td>{{ row.Name | title }}</td>
                    <td>{{ row.Age }}</td>
                    <td>${{ row.Salary | float | round(2) }}</td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
    <form method="post">
        <div class="form-group">
            <label>Prediction Input (0-1):</label>
            <input type="number" step="0.01" name="prediction" class="form-control" required>
        </div>
        <button type="submit" class="btn btn-primary">Predict</button>
    </form>
    {% if prediction is not none %}
        <p>Prediction Probability: {{ prediction | probability }}</p>
    {% endif %}
{% endblock %}

Output (/dashboard):

  • GET: Displays a table with Pandas data and a prediction form.
  • POST (prediction=0.75): Shows the table and Prediction Probability: 75.0%.

Explanation:

  • GET: Renders the dashboard with Pandas data.
  • POST: Processes the prediction input and displays the result.
  • | probability: Custom filter formats the prediction.

05. Best Practices for Handling GET and POST Requests

5.1 Recommended Practices

  • Use request.args.get and request.form.get: Provide defaults to handle missing data gracefully.
  • Validate Input: Check and sanitize user inputs to prevent errors or security issues.
  • Specify Methods: Explicitly define methods=['GET', 'POST'] for routes handling both.
  • Use CSRF Protection: For POST requests, use Flask-WTF or similar to prevent cross-site request forgery.
  • Provide Feedback: Display success/error messages in templates after POST requests.
  • Use url_for: Generate URLs dynamically in forms and links.

5.2 Security Considerations

  • Sanitize Inputs: Use libraries like bleach to clean user inputs.
  • Escape Outputs: Rely on Jinja2’s default escaping or | escape for user data.
  • File Uploads: Validate file types and sizes, and store files securely.

Example: Insecure Input Handling

File: app.py

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/comment', methods=['POST'])
def comment():
    comment = request.form['comment']
    return render_template('comment.html', comment=comment)

if __name__ == '__main__':
    app.run(debug=True)

File: templates/comment.html

<!DOCTYPE html>
<html>
<head><title>Comment</title></head>
<body>
    <p>{{ comment | safe }}</p>  <!-- Insecure -->
</body>
</html>

Input: comment=<script>alert('XSS')</script>

Output: Executes the script (XSS vulnerability).

Correct:

<!DOCTYPE html>
<html>
<head><title>Comment</title></head>
<body>
    <p>{{ comment | escape }}</p>  <!-- Safe -->
</body>
</html>

Output: Displays <script>alert('XSS')</script> safely.

Explanation:

  • Insecure - | safe with user input risks XSS.
  • Correct - | escape ensures safe rendering.

5.3 Practices to Avoid

  • Avoid Unvalidated Inputs: Always validate and sanitize user data.
  • Avoid Hardcoding URLs: Use url_for in forms and redirects.
  • Avoid Large File Uploads: Set size limits and validate file types.

06. Conclusion

Handling GET and POST requests in Flask is fundamental for building interactive, data-driven web applications, particularly for dashboards and ML interfaces. Key takeaways:

  • GET requests retrieve data, often with query parameters, using request.args.
  • POST requests submit data, such as forms or files, using request.form or request.files.
  • Integrate with Pandas for data display and form submissions for ML inputs.
  • Follow best practices like input validation, CSRF protection, and secure output rendering.

By mastering GET and POST request handling, you can create robust, secure, and user-friendly Flask applications that effectively manage data interactions in dynamic contexts!

Comments