You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

技术求助:如何在PyCharm中使用Flask、SQL、CSS、JavaScript、HTML创建带Google Keep式复选框的动态待办事项应用并存储清单当前价格至数据库

Hey Lucy, great choice going with Flask for your to-do app—super flexible and perfect for this kind of project. Let’s walk through building your Google Keep-style checklist with price tracking, step by step right in PyCharm.

1. Set Up Your PyCharm Project & Dependencies

First, let’s get your environment sorted in PyCharm:

  • Open PyCharm, create a new Pure Python project.
  • Set up a virtual environment (PyCharm will prompt you to do this automatically—just pick the Python version you prefer).
  • Install required packages via the terminal (or PyCharm’s package manager):
    pip install flask flask-sqlalchemy
    
    We’ll use flask-sqlalchemy for easy database integration without writing raw SQL—way faster for development.
2. Configure the Database & Define Your Todo Model

Create a new file app.py in your project root—this will be your main Flask app file. Let’s set up the database and define a Todo model that includes price tracking:

from flask import Flask, render_template, request, jsonify
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
# Configure SQLite database (easy for development; switch to PostgreSQL/MySQL later if needed)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todos.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

# Todo Model
class Todo(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    completed = db.Column(db.Boolean, default=False)
    price = db.Column(db.Float, nullable=False)  # Store price for each todo item

# Create database tables (run this once)
with app.app_context():
    db.create_all()
  • The completed field handles the checkbox state, and price stores your item’s price value.
  • Run this script once to generate the todos.db file in your project folder.
3. Build Flask Routes for Core Features

Add these routes to app.py to handle displaying todos, adding new ones, toggling completion, and deleting items:

# Home Route: Display all todos
@app.route('/')
def index():
    todos = Todo.query.all()
    return render_template('index.html', todos=todos)

# Add New Todo (AJAX-friendly for seamless updates)
@app.route('/add', methods=['POST'])
def add_todo():
    title = request.json.get('title')
    price = request.json.get('price')
    if title and price:
        new_todo = Todo(title=title, price=price)
        db.session.add(new_todo)
        db.session.commit()
        return jsonify({'id': new_todo.id, 'title': new_todo.title, 'price': new_todo.price, 'completed': False})
    return jsonify({'error': 'Missing title or price'}), 400

# Toggle Todo Completion
@app.route('/toggle/<int:todo_id>', methods=['POST'])
def toggle_todo(todo_id):
    todo = Todo.query.get_or_404(todo_id)
    todo.completed = not todo.completed
    db.session.commit()
    return jsonify({'completed': todo.completed})

# Delete Todo
@app.route('/delete/<int:todo_id>', methods=['POST'])
def delete_todo(todo_id):
    todo = Todo.query.get_or_404(todo_id)
    db.session.delete(todo)
    db.session.commit()
    return jsonify({'success': True})
  • We’re using JSON responses for AJAX calls so the UI updates without reloading the page—just like Google Keep’s smooth experience.
4. Build the Google Keep-Style UI (HTML/CSS/JS)

Create a templates folder in your project root, then make index.html inside it. This will handle the frontend with that familiar Keep checkbox style:

<!DOCTYPE html>
<html>
<head>
    <title>Google Keep-Style Todo List</title>
    <style>
        /* Google Keep-inspired checkbox */
        .todo-checkbox {
            appearance: none;
            width: 20px;
            height: 20px;
            border: 2px solid #ccc;
            border-radius: 50%;
            cursor: pointer;
            position: relative;
            margin-right: 10px;
        }
        .todo-checkbox:checked {
            border-color: #2196F3;
            background-color: #2196F3;
        }
        .todo-checkbox:checked::after {
            content: '✓';
            color: white;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-size: 14px;
            font-weight: bold;
        }
        /* Todo item styling */
        .todo-item {
            display: flex;
            align-items: center;
            padding: 12px;
            margin: 8px 0;
            border-radius: 8px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
            background: white;
        }
        .todo-completed {
            text-decoration: line-through;
            color: #666;
        }
        .todo-price {
            margin-left: auto;
            font-weight: bold;
            color: #333;
        }
        .delete-btn {
            margin-left: 10px;
            background: #ff4444;
            color: white;
            border: none;
            border-radius: 4px;
            padding: 4px 8px;
            cursor: pointer;
        }
        #add-todo-form {
            margin-bottom: 20px;
            display: flex;
            gap: 8px;
        }
        #add-todo-form input {
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 4px;
            flex: 1;
        }
        #add-todo-form button {
            background: #2196F3;
            color: white;
            border: none;
            border-radius: 4px;
            padding: 8px 16px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div style="max-width: 600px; margin: 20px auto;">
        <h1>My Todo List</h1>
        <form id="add-todo-form">
            <input type="text" id="todo-title" placeholder="Enter todo title" required>
            <input type="number" id="todo-price" placeholder="Enter price" step="0.01" required>
            <button type="submit">Add Todo</button>
        </form>
        <div id="todo-list">
            {% for todo in todos %}
            <div class="todo-item" data-id="{{ todo.id }}">
                <input type="checkbox" class="todo-checkbox" {% if todo.completed %}checked{% endif %}>
                <span class="todo-title {% if todo.completed %}todo-completed{% endif %}">{{ todo.title }}</span>
                <span class="todo-price">${{ "%.2f"|format(todo.price) }}</span>
                <button class="delete-btn">Delete</button>
            </div>
            {% endfor %}
        </div>
    </div>

    <script>
        // Add new todo
        document.getElementById('add-todo-form').addEventListener('submit', async (e) => {
            e.preventDefault();
            const title = document.getElementById('todo-title').value;
            const price = document.getElementById('todo-price').value;
            
            const response = await fetch('/add', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({title, price})
            });
            
            if (response.ok) {
                const todo = await response.json();
                const todoList = document.getElementById('todo-list');
                const todoItem = document.createElement('div');
                todoItem.className = 'todo-item';
                todoItem.dataset.id = todo.id;
                todoItem.innerHTML = `
                    <input type="checkbox" class="todo-checkbox">
                    <span class="todo-title">${todo.title}</span>
                    <span class="todo-price">$${todo.price.toFixed(2)}</span>
                    <button class="delete-btn">Delete</button>
                `;
                todoList.appendChild(todoItem);
                // Reset form
                document.getElementById('add-todo-form').reset();
            }
        });

        // Toggle todo completion
        document.getElementById('todo-list').addEventListener('change', async (e) => {
            if (e.target.classList.contains('todo-checkbox')) {
                const todoItem = e.target.closest('.todo-item');
                const todoId = todoItem.dataset.id;
                const response = await fetch(`/toggle/${todoId}`, {method: 'POST'});
                
                if (response.ok) {
                    const data = await response.json();
                    const titleSpan = todoItem.querySelector('.todo-title');
                    titleSpan.classList.toggle('todo-completed', data.completed);
                }
            }
        });

        // Delete todo
        document.getElementById('todo-list').addEventListener('click', async (e) => {
            if (e.target.classList.contains('delete-btn')) {
                const todoItem = e.target.closest('.todo-item');
                const todoId = todoItem.dataset.id;
                const response = await fetch(`/delete/${todoId}`, {method: 'POST'});
                
                if (response.ok) {
                    todoItem.remove();
                }
            }
        });
    </script>
</body>
</html>
  • The CSS mimics Google Keep’s rounded checkboxes with a blue fill when checked, plus soft shadows for that card-like feel.
  • JavaScript uses async/await to make AJAX calls, so the app feels responsive without page reloads.
5. Run & Test the App in PyCharm
  • In PyCharm, go to Run > Edit Configurations.
  • Click the + icon, select Flask Server.
  • Set the Target to your app.py file, and the Flask app to app:app.
  • Click Apply then Run.
  • Open your browser and go to http://localhost:5000—you’ll see your todo list in action!
Bonus Tips
  • For production, switch from SQLite to a more robust database like PostgreSQL or MySQL.
  • Add validation for price inputs (e.g., ensure they’re positive numbers).
  • Style the app further to match Google Keep’s exact look (warm accent colors, hover effects, etc.).

内容的提问来源于stack exchange,提问作者Lucy Sunday

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 06:40:05