如何在PyCharm中使用Flask、SQL、CSS、JavaScript、HTML创建含Google Keep风格复选框并支持价格存储的动态待办清单应用
Hey there! Let's walk through building your Google Keep-style todo app with Flask—complete with those satisfying toggleable checkboxes and database storage for tasks and their prices. I'll break this down into easy, actionable steps.
First, let's get the core tools installed. You'll need Flask for the web framework, and Flask-SQLAlchemy to simplify database interactions. Run this command in your terminal:
pip install flask flask-sqlalchemy
Create a main app file (let's call it app.py) and set up your Flask app with a database. We'll use SQLite for quick testing (you can swap it for PostgreSQL/MySQL later if needed).
Here's the code to define your todo model, which will store task content, price, and completion status:
from flask import Flask, render_template, request, redirect, url_for from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) # Configure SQLite database (file will be created in your project folder) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todos.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # Disable unnecessary warnings db = SQLAlchemy(app) class Todo(db.Model): id = db.Column(db.Integer, primary_key=True) content = db.Column(db.String(200), nullable=False) # Todo text price = db.Column(db.Float, nullable=False) # Associated price is_completed = db.Column(db.Boolean, default=False) # Checkbox state def __repr__(self): return f'<Todo {self.id}>' # Initialize the database (run this once in a Python shell) # from app import db; db.create_all() if __name__ == '__main__': app.run(debug=True)
To create the database file, open a Python shell in your project folder, run from app import db; db.create_all(), then exit the shell.
Google Keep's checkboxes toggle state without page reloads, so we'll use JavaScript + AJAX for that smooth experience. Create a templates folder, then add an index.html file with this code:
<!DOCTYPE html> <html> <head> <title>Keep-Style Todo List</title> <style> body { font-family: Arial, sans-serif; max-width: 800px; margin: 2rem auto; padding: 0 1rem; background-color: #f5f5f5; } .todo-card { background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); padding: 1rem; margin: 1rem 0; display: flex; align-items: center; gap: 1rem; } .todo-checkbox { width: 20px; height: 20px; cursor: pointer; accent-color: #4285f4; } .todo-content { flex: 1; font-size: 1rem; text-decoration: {{ 'line-through' if todo.is_completed else 'none' }}; opacity: {{ 0.6 if todo.is_completed else 1 }}; transition: all 0.2s ease; } .todo-price { color: #666; font-size: 0.9rem; } .add-form { margin: 2rem 0; display: flex; gap: 0.5rem; } .add-form input { padding: 0.75rem; border: 1px solid #ddd; border-radius: 4px; flex: 1; font-size: 1rem; } .add-form button { padding: 0.75rem 1.5rem; background-color: #4285f4; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 1rem; } </style> </head> <body> <h1>My Todo List</h1> <!-- Form to add new todos --> <form class="add-form" method="POST" action="{{ url_for('add_todo') }}"> <input type="text" name="content" placeholder="Enter task..." required> <input type="number" step="0.01" name="price" placeholder="Enter price..." required> <button type="submit">Add Todo</button> </form> <!-- Render all todos --> {% for todo in todos %} <div class="todo-card"> <input type="checkbox" class="todo-checkbox" data-id="{{ todo.id }}" {{ 'checked' if todo.is_completed else '' }} > <div class="todo-content">{{ todo.content }}</div> <div class="todo-price">${{ "%.2f"|format(todo.price) }}</div> </div> {% endfor %} <script> // Toggle todo completion status via AJAX document.querySelectorAll('.todo-checkbox').forEach(checkbox => { checkbox.addEventListener('change', function() { const todoId = this.getAttribute('data-id'); const isCompleted = this.checked; fetch(`/toggle/${todoId}`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({is_completed: isCompleted}), }) .then(response => response.json()) .then(data => { // Update text style without reloading const contentElement = this.nextElementSibling; if (isCompleted) { contentElement.style.textDecoration = 'line-through'; contentElement.style.opacity = '0.6'; } else { contentElement.style.textDecoration = 'none'; contentElement.style.opacity = '1'; } }); }); }); </script> </body> </html>
Now add these routes to your app.py file to handle displaying todos, adding new ones, and toggling completion status:
@app.route('/') def index(): # Get all todos from the database todos = Todo.query.all() return render_template('index.html', todos=todos) @app.route('/add', methods=['POST']) def add_todo(): # Get form data content = request.form['content'] price = float(request.form['price']) # Create new todo entry new_todo = Todo(content=content, price=price) try: db.session.add(new_todo) db.session.commit() return redirect(url_for('index')) except: return 'There was an issue adding your task' @app.route('/toggle/<int:id>', methods=['POST']) def toggle_todo(id): # Find the todo by ID todo = Todo.query.get_or_404(id) # Update completion status from AJAX request data = request.get_json() todo.is_completed = data['is_completed'] try: db.session.commit() return {'success': True} except: return {'success': False}, 500
Start your Flask app by running:
python app.py
Visit http://localhost:5000 in your browser—you’ll see a clean, Keep-style todo list where you can add tasks with prices, and toggle checkboxes without page reloads!
Quick Optimizations to Enhance the App
- Add a delete button for todos (similar to the toggle logic with AJAX)
- Customize the checkbox style to match Google Keep’s circular design
- Add edit functionality for task content and prices
- For production, switch to a robust database like PostgreSQL and disable debug mode
内容的提问来源于stack exchange,提问作者Lucy Sunday




