如何在Flask网页中实现按钮点击后状态永久保存?
Hey there! Let's fix that persistent button state issue for your CS50 final project. Right now, you're only updating the button's look client-side—when you refresh, there's no server-side data to pull the saved state from. Here's a step-by-step solution to make those statuses stick using your Flask/SQL setup:
1. First, Set Up a Database Table to Track Statuses
You'll need a table to store each user's application status per firm and role type. Run this SQL query to create it (assuming you already have a users table for authenticated users and firm_names has an id primary key):
CREATE TABLE application_status ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, firm_id INTEGER NOT NULL, application_type TEXT NOT NULL, -- Matches your button names: spring, asset, ib, etc. status INTEGER NOT NULL DEFAULT 0, -- 0 = Not started, 1 = In progress, 2 = Applied FOREIGN KEY(user_id) REFERENCES users(id), FOREIGN KEY(firm_id) REFERENCES firm_names(id), UNIQUE(user_id, firm_id, application_type) -- Prevent duplicate entries for the same user/firm/role );
2. Modify Your Flask Route to Fetch Saved Statuses
Update the /summertable route to pull existing statuses for the logged-in user and pass them to the template:
@app.route("/summertable") @login_required def summertable(): # Get firms + their saved statuses for the current user (use LEFT JOIN to include firms with no status yet) firms = db.execute(""" SELECT fn.id, fn.firm_name, COALESCE(spring.status, 0) AS spring_status, COALESCE(asset.status, 0) AS asset_status, COALESCE(ib.status, 0) AS ib_status, COALESCE(risk.status, 0) AS risk_status, COALESCE(markets.status, 0) AS markets_status FROM firm_names fn LEFT JOIN application_status spring ON fn.id = spring.firm_id AND spring.user_id = ? AND spring.application_type = 'spring' LEFT JOIN application_status asset ON fn.id = asset.firm_id AND asset.user_id = ? AND asset.application_type = 'asset' LEFT JOIN application_status ib ON fn.id = ib.firm_id AND ib.user_id = ? AND ib.application_type = 'ib' LEFT JOIN application_status risk ON fn.id = risk.firm_id AND risk.user_id = ? AND risk.application_type = 'risk' LEFT JOIN application_status markets ON fn.id = markets.firm_id AND markets.user_id = ? AND markets.application_type = 'markets' """, (session["user_id"],) * 5) # Map status codes to human-readable text and colors for easy template use status_map = { 0: {"text": "Not started", "color": ""}, 1: {"text": "In progress...", "color": "LightSalmon"}, 2: {"text": "Applied", "color": "PaleGreen"} } return render_template("table.html", firms=firms, status_map=status_map)
3. Update the HTML Template to Render Saved States
Modify your table buttons to load the initial status from the database, and add data attributes to pass firm/role info to JavaScript:
{% extends "layout.html" %} {% block title %} Application Tracker {% endblock %} {% block main %} <table class="table-striped tracker" style="table-align" id="tracker"> <tr> <th style="text-align:left">Firms</th> <th>Spring weeks</th> <th>Asset management</th> <th>Investment banking</th> <th>Risk</th> <th>Global Markets</th> </tr> {% for firm in firms %} <tr> <td>{{ firm.firm_name }}</td> <!-- Spring Weeks Button --> <td> <button onclick="updateStatus(this)" type="button" class="btn button" data-firm-id="{{ firm.id }}" data-type="spring" data-current-status="{{ firm.spring_status }}" style="background-color: {{ status_map[firm.spring_status].color }}"> {{ status_map[firm.spring_status].text }} </button> </td> <!-- Asset Management Button --> <td> <button onclick="updateStatus(this)" type="button" class="btn button" data-firm-id="{{ firm.id }}" data-type="asset" data-current-status="{{ firm.asset_status }}" style="background-color: {{ status_map[firm.asset_status].color }}"> {{ status_map[firm.asset_status].text }} </button> </td> <!-- Investment Banking Button --> <td> <button onclick="updateStatus(this)" type="button" class="btn button" data-firm-id="{{ firm.id }}" data-type="ib" data-current-status="{{ firm.ib_status }}" style="background-color: {{ status_map[firm.ib_status].color }}"> {{ status_map[firm.ib_status].text }} </button> </td> <!-- Risk Button --> <td> <button onclick="updateStatus(this)" type="button" class="btn button" data-firm-id="{{ firm.id }}" data-type="risk" data-current-status="{{ firm.risk_status }}" style="background-color: {{ status_map[firm.risk_status].color }}"> {{ status_map[firm.risk_status].text }} </button> </td> <!-- Global Markets Button --> <td> <button onclick="updateStatus(this)" type="button" class="btn button" data-firm-id="{{ firm.id }}" data-type="markets" data-current-status="{{ firm.markets_status }}" style="background-color: {{ status_map[firm.markets_status].color }}"> {{ status_map[firm.markets_status].text }} </button> </td> </tr> {% endfor %} </table> <script> // Match the status mapping from your Flask route const statusMap = { 0: { text: "Not started", color: "" }, 1: { text: "In progress...", color: "LightSalmon" }, 2: { text: "Applied", color: "PaleGreen" } }; async function updateStatus(button) { let currentStatus = parseInt(button.dataset.currentStatus); // Cycle through statuses: 0 →1 →2 →0 let nextStatus = (currentStatus + 1) % 3; // Update button UI immediately for better user experience button.textContent = statusMap[nextStatus].text; button.style.backgroundColor = statusMap[nextStatus].color; button.dataset.currentStatus = nextStatus; // Send update to server to save in database try { const response = await fetch("/update_status", { method: "POST", headers: { "Content-Type": "application/json", "X-CSRFToken": "{{ csrf_token() }}" // Include this if you use Flask-WTF for CSRF protection }, body: JSON.stringify({ firm_id: button.dataset.firmId, type: button.dataset.type, status: nextStatus }) }); if (!response.ok) { // Revert button if server update fails button.textContent = statusMap[currentStatus].text; button.style.backgroundColor = statusMap[currentStatus].color; button.dataset.currentStatus = currentStatus; alert("Failed to save status. Please try again."); } } catch (error) { // Revert button on network error button.textContent = statusMap[currentStatus].text; button.style.backgroundColor = statusMap[currentStatus].color; button.dataset.currentStatus = currentStatus; alert("Network error. Please check your connection."); } } </script> {% endblock %}
4. Add a Flask Route to Handle Status Updates
Create a POST route to save the new status to the database (use INSERT OR REPLACE to create or update entries):
from flask import request, jsonify @app.route("/update_status", methods=["POST"]) @login_required def update_status(): data = request.get_json() firm_id = data.get("firm_id") app_type = data.get("type") status = data.get("status") # Validate input if not firm_id or not app_type or status is None: return jsonify({"success": False, "message": "Missing required data"}), 400 # Insert or replace the status entry for the user/firm/role db.execute(""" INSERT OR REPLACE INTO application_status (user_id, firm_id, application_type, status) VALUES (?, ?, ?, ?) """, (session["user_id"], firm_id, app_type, status)) db.commit() return jsonify({"success": True})
Quick Notes to Keep in Mind
- Make sure your
firm_namestable has anidprimary key (if it doesn't, add it withALTER TABLE firm_names ADD COLUMN id INTEGER PRIMARY KEY AUTOINCREMENT;). - If you're not using Flask-WTF for CSRF protection, you can skip the
X-CSRFTokenheader, but it's recommended to add CSRF safeguards for production. - Test by clicking a button, refreshing the page—your status should now persist!
内容的提问来源于stack exchange,提问作者tobywinn

