GET vs POST, form submissions, and how data moves between browser and Python
Day 27 of 80
request.form worksGET — "Give me a page." When you type a URL in your browser, that's a GET request. Data goes in the URL itself: /?search=aerial&platform=kling. GET requests are for reading. They should never change anything on the server.
POST — "Here's some data, process it." When you submit a form, that's a POST request. Data goes in the request body, not the URL. Use POST for adding or changing data — creating a new prompt, deleting a record, submitting a login.
The rule of thumb: GET = read, POST = write.
from flask import Flask, render_template, request
app = Flask(__name__)
# This route only handles GET requests (the default)
@app.route("/")
def home():
return render_template("index.html")
# This route handles POST requests from a form submission
@app.route("/add", methods=["POST"])
def add_prompt():
# request.form is a dictionary of the submitted form fields
platform = request.form["platform"]
shot = request.form["shot"]
print(f"Received: {platform} — {shot}")
return redirect("/") # send the user back to the home page
methods=["POST"] — By default Flask routes only accept GET. You have to explicitly opt in to POST. You can also do methods=["GET", "POST"] to handle both in one function — Corey shows this pattern in the video.
request.form["platform"] — Flask gives you a global request object whenever a request is being handled. request.form is a dictionary containing everything submitted in the form body. The key matches the name attribute on the HTML input.
<!-- action="/add" — where to send the data -->
<!-- method="post" — how to send it (POST request body) -->
<form action="/add" method="post">
<!-- name="platform" matches request.form["platform"] in Python -->
<select name="platform">
<option value="Kling">Kling</option>
<option value="Runway">Runway</option>
<option value="Veo">Veo</option>
</select>
<input type="text" name="shot" placeholder="Shot description...">
<textarea name="prompt" placeholder="Prompt text..."></textarea>
<button type="submit">Add Prompt</button>
</form>
name attribute on every input is the key you'll use in request.form. If the input has name="shot", you read it with request.form["shot"]. If the name doesn't match exactly, you'll get a KeyError. Use request.form.get("shot", "") for a safer version that returns an empty string instead of crashing if the field is missing.
WTForms, form validation, POST handling (~30 min)
SQLAlchemy intro (~25 min) — you'll use JSON files instead, but the concepts transfer
Corey uses SQLAlchemy for database storage. You won't use a database — you'll keep storing prompts in your JSON file, the same helpers.py functions from Week 5. But watch how he structures models and CRUD operations. The pattern (create, read, update, delete) is identical. You're just swapping the storage layer.
| Resource | What to focus on |
|---|---|
| Flask Quickstart — Routing | Variable rules (<int:index>, <string:name>), HTTP methods, URL building with url_for() |
Pay special attention to variable rules like @app.route("/delete/<int:index>"). The <int:index> part captures a number from the URL and passes it to your function as an argument. You'll use this tomorrow for the delete button: /delete/0 deletes prompt at index 0, /delete/3 deletes index 3. Flask handles the URL parsing for you.
After watching the videos, build this small app in your flask-practice/ folder. It's a simplified version of what you'll build tomorrow:
from flask import Flask, render_template, request, redirect
app = Flask(__name__)
# In-memory list — resets every time the server restarts
# Tomorrow you'll replace this with your JSON helpers
items = []
@app.route("/")
def home():
return render_template("form_test.html", items=items)
@app.route("/add", methods=["POST"])
def add_item():
text = request.form.get("text", "")
if text:
items.append(text)
return redirect("/")
@app.route("/delete/<int:index>")
def delete_item(index):
if 0 <= index < len(items):
items.pop(index)
return redirect("/")
if __name__ == "__main__":
app.run(debug=True)
request.form.get("text", "") — The safer version of request.form["text"]. If the field is missing, returns "" instead of raising a KeyError. Use .get() when in doubt.
redirect("/") — After processing a POST, always redirect instead of rendering a template directly. This prevents the browser's "resubmit form" warning when the user hits the back button. This pattern is called Post/Redirect/Get (PRG).
After a POST request, always call redirect() to send the user to a GET route. If you render a template directly after a POST and the user hits the browser back button or refreshes, the browser will try to resubmit the form — which means your data gets written twice.
Pattern: POST route does the work → redirect("/"); → GET route renders the updated page. Every web framework follows this pattern.
request.form["field_name"] doesredirect() after a POSTform_test.py app runs and you can add and delete itemsTomorrow you build the actual Prompt Vault web app. You'll reuse helpers.py from Week 5 as-is, add a proper app.py with three routes, and build a dark-themed HTML template. Your CLI prompt tool becomes a browser app.