优秀的编程知识分享平台

网站首页 > 技术文章 正文

Python疯狂练习60天——第十五天_疯狂python讲义电子版

nanyue 2025-10-14 02:13:41 技术文章 1 ℃

今日练习主题:Web开发基础

今天我们将学习Python Web开发的基础知识,包括Flask框架、路由、模板、表单处理、数据库集成等。

练习1:Flask基础应用

from flask import Flask, render_template, request, redirect, url_for, flash, jsonify
import os
from datetime import datetime

# 创建Flask应用
app = Flask(__name__)
app.secret_key = 'your-secret-key-here'  # 用于会话安全

# 简单的内存数据库(实际项目中请使用真实数据库)
users_db = []
posts_db = []

@app.route('/')
def index():
    """首页"""
    return render_template('index.html', 
                         title='首页',
                         current_time=datetime.now())

@app.route('/about')
def about():
    """关于页面"""
    return '''
    <h1>关于我们</h1>
    <p>这是一个使用Flask构建的示例网站。</p>
    <a href="/">返回首页</a>
    '''

@app.route('/hello/<name>')
def hello(name):
    """带参数的路由"""
    return f'''
    <h1>Hello, {name}!</h1>
    <p>欢迎来到我们的网站!</p>
    <a href="/">返回首页</a>
    '''

@app.route('/user/<int:user_id>')
def user_profile(user_id):
    """带整数参数的路由"""
    # 模拟从数据库获取用户信息
    user = next((u for u in users_db if u['id'] == user_id), None)
    if user:
        return f'''
        <h1>用户资料</h1>
        <p>ID: {user['id']}</p>
        <p>姓名: {user['name']}</p>
        <p>邮箱: {user['email']}</p>
        <a href="/users">返回用户列表</a>
        '''
    else:
        return '<h1>用户不存在</h1>', 404

@app.route('/api/users')
def api_users():
    """JSON API接口"""
    return jsonify({
        'status': 'success',
        'count': len(users_db),
        'users': users_db
    })

@app.route('/api/users/<int:user_id>')
def api_user_detail(user_id):
    """用户详情API"""
    user = next((u for u in users_db if u['id'] == user_id), None)
    if user:
        return jsonify({'status': 'success', 'user': user})
    else:
        return jsonify({'status': 'error', 'message': 'User not found'}), 404

def create_app():
    """应用工厂函数"""
    # 确保模板目录存在
    os.makedirs('templates', exist_ok=True)
    
    # 创建基础模板
    with open('templates/base.html', 'w', encoding='utf-8') as f:
        f.write('''
        <!DOCTYPE html>
        <html>
        <head>
            <title>{% block title %}My Website{% endblock %}</title>
            <style>
                body { font-family: Arial, sans-serif; margin: 40px; }
                nav { background: #f0f0f0; padding: 10px; margin-bottom: 20px; }
                .flash-message { padding: 10px; margin: 10px 0; border-radius: 5px; }
                .success { background: #d4edda; color: #155724; }
                .error { background: #f8d7da; color: #721c24; }
            </style>
        </head>
        <body>
            <nav>
                <a href="/">首页</a> |
                <a href="/about">关于</a> |
                <a href="/users">用户</a> |
                <a href="/posts">文章</a>
            </nav>
            
            {% with messages = get_flashed_messages(with_categories=true) %}
                {% if messages %}
                    {% for category, message in messages %}
                        <div class="flash-message {{ category }}">{{ message }}</div>
                    {% endfor %}
                {% endif %}
            {% endwith %}
            
            {% block content %}{% endblock %}
        </body>
        </html>
        ''')
    
    # 创建首页模板
    with open('templates/index.html', 'w', encoding='utf-8') as f:
        f.write('''
        {% extends "base.html" %}
        
        {% block title %}{{ title }}{% endblock %}
        
        {% block content %}
        <h1>欢迎来到我的网站</h1>
        <p>当前时间: {{ current_time.strftime('%Y-%m-%d %H:%M:%S') }}</p>
        
        <h2>快速链接</h2>
        <ul>
            <li><a href="/hello/World">打招呼</a></li>
            <li><a href="/users">用户管理</a></li>
            <li><a href="/posts">文章列表</a></li>
            <li><a href="/api/users">用户API</a></li>
        </ul>
        
        <h2>网站功能</h2>
        <p>这个网站演示了Flask的以下功能:</p>
        <ul>
            <li>路由和视图函数</li>
            <li>模板渲染</li>
            <li>表单处理</li>
            <li>会话和消息闪现</li>
            <li>RESTful API</li>
        </ul>
        {% endblock %}
        ''')
    
    return app

# 运行应用
if __name__ == '__main__':
    app = create_app()
    # 只在开发环境中使用debug模式
    app.run(debug=True, host='0.0.0.0', port=5000)

练习2:用户管理功能

from flask import Flask, render_template, request, redirect, url_for, flash
import os

app = Flask(__name__)
app.secret_key = 'user-management-secret'

# 用户数据库(内存存储)
users_db = []
next_user_id = 1

@app.route('/users')
def user_list():
    """用户列表页面"""
    return render_template('users.html', users=users_db)

@app.route('/users/add', methods=['GET', 'POST'])
def add_user():
    """添加用户"""
    if request.method == 'POST':
        global next_user_id
        
        name = request.form.get('name', '').strip()
        email = request.form.get('email', '').strip()
        age = request.form.get('age', '').strip()
        
        # 基本验证
        if not name or not email:
            flash('姓名和邮箱是必填项', 'error')
            return render_template('add_user.html')
        
        # 检查邮箱是否已存在
        if any(user['email'] == email for user in users_db):
            flash('邮箱已存在', 'error')
            return render_template('add_user.html')
        
        # 添加用户
        user = {
            'id': next_user_id,
            'name': name,
            'email': email,
            'age': int(age) if age.isdigit() else None,
            'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        }
        users_db.append(user)
        next_user_id += 1
        
        flash(f'用户 {name} 添加成功!', 'success')
        return redirect(url_for('user_list'))
    
    return render_template('add_user.html')

@app.route('/users/edit/<int:user_id>', methods=['GET', 'POST'])
def edit_user(user_id):
    """编辑用户"""
    user = next((u for u in users_db if u['id'] == user_id), None)
    if not user:
        flash('用户不存在', 'error')
        return redirect(url_for('user_list'))
    
    if request.method == 'POST':
        name = request.form.get('name', '').strip()
        email = request.form.get('email', '').strip()
        age = request.form.get('age', '').strip()
        
        if not name or not email:
            flash('姓名和邮箱是必填项', 'error')
            return render_template('edit_user.html', user=user)
        
        # 检查邮箱是否被其他用户使用
        if any(u['email'] == email and u['id'] != user_id for u in users_db):
            flash('邮箱已被其他用户使用', 'error')
            return render_template('edit_user.html', user=user)
        
        # 更新用户信息
        user['name'] = name
        user['email'] = email
        user['age'] = int(age) if age.isdigit() else None
        
        flash(f'用户 {name} 更新成功!', 'success')
        return redirect(url_for('user_list'))
    
    return render_template('edit_user.html', user=user)

@app.route('/users/delete/<int:user_id>')
def delete_user(user_id):
    """删除用户"""
    global users_db
    user = next((u for u in users_db if u['id'] == user_id), None)
    
    if user:
        users_db = [u for u in users_db if u['id'] != user_id]
        flash(f'用户 {user["name"]} 删除成功!', 'success')
    else:
        flash('用户不存在', 'error')
    
    return redirect(url_for('user_list'))

def create_user_templates():
    """创建用户管理相关的模板"""
    os.makedirs('templates', exist_ok=True)
    
    # 用户列表模板
    with open('templates/users.html', 'w', encoding='utf-8') as f:
        f.write('''
        {% extends "base.html" %}
        
        {% block title %}用户管理{% endblock %}
        
        {% block content %}
        <h1>用户管理</h1>
        
        <a href="{{ url_for('add_user') }}" style="background: #007bff; color: white; padding: 10px; text-decoration: none; border-radius: 5px;">
            添加用户
        </a>
        
        {% if users %}
            <table border="1" style="width: 100%; margin-top: 20px; border-collapse: collapse;">
                <thead>
                    <tr style="background: #f8f9fa;">
                        <th style="padding: 10px;">ID</th>
                        <th>姓名</th>
                        <th>邮箱</th>
                        <th>年龄</th>
                        <th>注册时间</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    {% for user in users %}
                    <tr>
                        <td style="padding: 10px; text-align: center;">{{ user.id }}</td>
                        <td>{{ user.name }}</td>
                        <td>{{ user.email }}</td>
                        <td style="text-align: center;">{{ user.age if user.age else '-' }}</td>
                        <td>{{ user.created_at }}</td>
                        <td style="text-align: center;">
                            <a href="{{ url_for('edit_user', user_id=user.id) }}">编辑</a> |
                            <a href="{{ url_for('delete_user', user_id=user.id) }}" onclick="return confirm('确定删除吗?')">删除</a>
                        </td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        {% else %}
            <p style="margin-top: 20px;">还没有用户,<a href="{{ url_for('add_user') }}">点击添加</a></p>
        {% endif %}
        {% endblock %}
        ''')
    
    # 添加用户模板
    with open('templates/add_user.html', 'w', encoding='utf-8') as f:
        f.write('''
        {% extends "base.html" %}
        
        {% block title %}添加用户{% endblock %}
        
        {% block content %}
        <h1>添加用户</h1>
        
        <form method="POST" style="max-width: 400px;">
            <div style="margin-bottom: 15px;">
                <label style="display: block; margin-bottom: 5px;">姓名:</label>
                <input type="text" name="name" required style="width: 100%; padding: 8px;">
            </div>
            
            <div style="margin-bottom: 15px;">
                <label style="display: block; margin-bottom: 5px;">邮箱:</label>
                <input type="email" name="email" required style="width: 100%; padding: 8px;">
            </div>
            
            <div style="margin-bottom: 15px;">
                <label style="display: block; margin-bottom: 5px;">年龄:</label>
                <input type="number" name="age" min="1" max="150" style="width: 100%; padding: 8px;">
            </div>
            
            <button type="submit" style="background: #28a745; color: white; padding: 10px 20px; border: none; border-radius: 5px;">添加</button>
            <a href="{{ url_for('user_list') }}" style="margin-left: 10px;">取消</a>
        </form>
        {% endblock %}
        ''')
    
    # 编辑用户模板
    with open('templates/edit_user.html', 'w', encoding='utf-8') as f:
        f.write('''
        {% extends "base.html" %}
        
        {% block title %}编辑用户{% endblock %}
        
        {% block content %}
        <h1>编辑用户</h1>
        
        <form method="POST" style="max-width: 400px;">
            <div style="margin-bottom: 15px;">
                <label style="display: block; margin-bottom: 5px;">姓名:</label>
                <input type="text" name="name" value="{{ user.name }}" required style="width: 100%; padding: 8px;">
            </div>
            
            <div style="margin-bottom: 15px;">
                <label style="display: block; margin-bottom: 5px;">邮箱:</label>
                <input type="email" name="email" value="{{ user.email }}" required style="width: 100%; padding: 8px;">
            </div>
            
            <div style="margin-bottom: 15px;">
                <label style="display: block; margin-bottom: 5px;">年龄:</label>
                <input type="number" name="age" value="{{ user.age if user.age else '' }}" min="1" max="150" style="width: 100%; padding: 8px;">
            </div>
            
            <button type="submit" style="background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 5px;">更新</button>
            <a href="{{ url_for('user_list') }}" style="margin-left: 10px;">取消</a>
        </form>
        {% endblock %}
        ''')

# 运行用户管理应用
if __name__ == '__main__':
    create_user_templates()
    app.run(debug=True, port=5001)

练习3:博客系统

from flask import Flask, render_template, request, redirect, url_for, flash
from datetime import datetime
import os

app = Flask(__name__)
app.secret_key = 'blog-system-secret'

# 博客文章数据库(内存存储)
posts_db = []
next_post_id = 1

@app.route('/')
def home():
    """博客首页"""
    # 按创建时间倒序排列
    sorted_posts = sorted(posts_db, key=lambda x: x['created_at'], reverse=True)
    return render_template('blog_home.html', posts=sorted_posts)

@app.route('/post/<int:post_id>')
def view_post(post_id):
    """查看文章详情"""
    post = next((p for p in posts_db if p['id'] == post_id), None)
    if not post:
        flash('文章不存在', 'error')
        return redirect(url_for('home'))
    
    return render_template('view_post.html', post=post)

@app.route('/post/add', methods=['GET', 'POST'])
def add_post():
    """添加文章"""
    if request.method == 'POST':
        global next_post_id
        
        title = request.form.get('title', '').strip()
        content = request.form.get('content', '').strip()
        author = request.form.get('author', '匿名').strip()
        
        if not title or not content:
            flash('标题和内容是必填项', 'error')
            return render_template('add_post.html')
        
        # 添加文章
        post = {
            'id': next_post_id,
            'title': title,
            'content': content,
            'author': author,
            'created_at': datetime.now(),
            'views': 0
        }
        posts_db.append(post)
        next_post_id += 1
        
        flash('文章发布成功!', 'success')
        return redirect(url_for('home'))
    
    return render_template('add_post.html')

@app.route('/post/edit/<int:post_id>', methods=['GET', 'POST'])
def edit_post(post_id):
    """编辑文章"""
    post = next((p for p in posts_db if p['id'] == post_id), None)
    if not post:
        flash('文章不存在', 'error')
        return redirect(url_for('home'))
    
    if request.method == 'POST':
        title = request.form.get('title', '').strip()
        content = request.form.get('content', '').strip()
        author = request.form.get('author', '').strip()
        
        if not title or not content:
            flash('标题和内容是必填项', 'error')
            return render_template('edit_post.html', post=post)
        
        # 更新文章
        post['title'] = title
        post['content'] = content
        post['author'] = author
        
        flash('文章更新成功!', 'success')
        return redirect(url_for('view_post', post_id=post_id))
    
    return render_template('edit_post.html', post=post)

@app.route('/post/delete/<int:post_id>')
def delete_post(post_id):
    """删除文章"""
    global posts_db
    post = next((p for p in posts_db if p['id'] == post_id), None)
    
    if post:
        posts_db = [p for p in posts_db if p['id'] != post_id]
        flash('文章删除成功!', 'success')
    else:
        flash('文章不存在', 'error')
    
    return redirect(url_for('home'))

def create_blog_templates():
    """创建博客相关的模板"""
    os.makedirs('templates', exist_ok=True)
    
    # 博客首页模板
    with open('templates/blog_home.html', 'w', encoding='utf-8') as f:
        f.write('''
        {% extends "base.html" %}
        
        {% block title %}我的博客{% endblock %}
        
        {% block content %}
        <h1>我的博客</h1>
        
        <a href="{{ url_for('add_post') }}" style="background: #28a745; color: white; padding: 10px; text-decoration: none; border-radius: 5px;">
            写文章
        </a>
        
        {% if posts %}
            <div style="margin-top: 30px;">
                {% for post in posts %}
                <div style="border: 1px solid #ddd; padding: 20px; margin-bottom: 20px; border-radius: 5px;">
                    <h3><a href="{{ url_for('view_post', post_id=post.id) }}" style="text-decoration: none; color: #333;">{{ post.title }}</a></h3>
                    <p style="color: #666;">
                        作者: {{ post.author }} | 
                        发布时间: {{ post.created_at.strftime('%Y-%m-%d %H:%M') }} |
                        阅读量: {{ post.views }}
                    </p>
                    <p>{{ post.content[:200] }}{% if post.content|length > 200 %}...{% endif %}</p>
                    <div>
                        <a href="{{ url_for('view_post', post_id=post.id) }}">阅读全文</a> |
                        <a href="{{ url_for('edit_post', post_id=post.id) }}">编辑</a> |
                        <a href="{{ url_for('delete_post', post_id=post.id) }}" onclick="return confirm('确定删除吗?')">删除</a>
                    </div>
                </div>
                {% endfor %}
            </div>
        {% else %}
            <p style="margin-top: 20px;">还没有文章,<a href="{{ url_for('add_post') }}">点击创建第一篇</a></p>
        {% endif %}
        {% endblock %}
        ''')
    
    # 查看文章模板
    with open('templates/view_post.html', 'w', encoding='utf-8') as f:
        f.write('''
        {% extends "base.html" %}
        
        {% block title %}{{ post.title }}{% endblock %}
        
        {% block content %}
        <article>
            <h1>{{ post.title }}</h1>
            <div style="color: #666; margin-bottom: 20px;">
                作者: {{ post.author }} | 
                发布时间: {{ post.created_at.strftime('%Y-%m-%d %H:%M') }} |
                阅读量: {{ post.views }}
            </div>
            
            <div style="line-height: 1.6; font-size: 16px;">
                {{ post.content|replace('\\n', '<br>')|safe }}
            </div>
            
            <div style="margin-top: 30px;">
                <a href="{{ url_for('home') }}">返回列表</a> |
                <a href="{{ url_for('edit_post', post_id=post.id) }}">编辑</a> |
                <a href="{{ url_for('delete_post', post_id=post.id) }}" onclick="return confirm('确定删除吗?')">删除</a>
            </div>
        </article>
        {% endblock %}
        ''')
    
    # 添加文章模板
    with open('templates/add_post.html', 'w', encoding='utf-8') as f:
        f.write('''
        {% extends "base.html" %}
        
        {% block title %}写文章{% endblock %}
        
        {% block content %}
        <h1>写文章</h1>
        
        <form method="POST" style="max-width: 800px;">
            <div style="margin-bottom: 15px;">
                <label style="display: block; margin-bottom: 5px;">标题:</label>
                <input type="text" name="title" required style="width: 100%; padding: 8px; font-size: 16px;">
            </div>
            
            <div style="margin-bottom: 15px;">
                <label style="display: block; margin-bottom: 5px;">作者:</label>
                <input type="text" name="author" value="匿名" style="width: 300px; padding: 8px;">
            </div>
            
            <div style="margin-bottom: 15px;">
                <label style="display: block; margin-bottom: 5px;">内容:</label>
                <textarea name="content" required style="width: 100%; height: 300px; padding: 8px; font-size: 14px; font-family: Arial, sans-serif;"></textarea>
            </div>
            
            <button type="submit" style="background: #28a745; color: white; padding: 10px 20px; border: none; border-radius: 5px; font-size: 16px;">发布</button>
            <a href="{{ url_for('home') }}" style="margin-left: 10px;">取消</a>
        </form>
        {% endblock %}
        ''')
    
    # 编辑文章模板
    with open('templates/edit_post.html', 'w', encoding='utf-8') as f:
        f.write('''
        {% extends "base.html" %}
        
        {% block title %}编辑文章{% endblock %}
        
        {% block content %}
        <h1>编辑文章</h1>
        
        <form method="POST" style="max-width: 800px;">
            <div style="margin-bottom: 15px;">
                <label style="display: block; margin-bottom: 5px;">标题:</label>
                <input type="text" name="title" value="{{ post.title }}" required style="width: 100%; padding: 8px; font-size: 16px;">
            </div>
            
            <div style="margin-bottom: 15px;">
                <label style="display: block; margin-bottom: 5px;">作者:</label>
                <input type="text" name="author" value="{{ post.author }}" style="width: 300px; padding: 8px;">
            </div>
            
            <div style="margin-bottom: 15px;">
                <label style="display: block; margin-bottom: 5px;">内容:</label>
                <textarea name="content" required style="width: 100%; height: 300px; padding: 8px; font-size: 14px; font-family: Arial, sans-serif;">{{ post.content }}</textarea>
            </div>
            
            <button type="submit" style="background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 5px; font-size: 16px;">更新</button>
            <a href="{{ url_for('view_post', post_id=post.id) }}" style="margin-left: 10px;">取消</a>
        </form>
        {% endblock %}
        ''')

# 运行博客系统
if __name__ == '__main__':
    create_blog_templates()
    app.run(debug=True, port=5002)

练习4:RESTful API

from flask import Flask, request, jsonify
from flask_restful import Api, Resource, reqparse, abort

app = Flask(__name__)
api = Api(app)

# 模拟数据库
books_db = []
next_book_id = 1

# 请求参数解析器
book_parser = reqparse.RequestParser()
book_parser.add_argument('title', type=str, required=True, help='书名不能为空')
book_parser.add_argument('author', type=str, required=True, help='作者不能为空')
book_parser.add_argument('year', type=int, help='出版年份必须是整数')
book_parser.add_argument('price', type=float, help='价格必须是数字')

class BookListResource(Resource):
    """图书列表资源"""
    
    def get(self):
        """获取所有图书"""
        return {
            'status': 'success',
            'count': len(books_db),
            'books': books_db
        }
    
    def post(self):
        """创建新图书"""
        global next_book_id
        
        args = book_parser.parse_args()
        
        # 创建新图书
        book = {
            'id': next_book_id,
            'title': args['title'],
            'author': args['author'],
            'year': args.get('year'),
            'price': args.get('price'),
            'created_at': datetime.now().isoformat()
        }
        
        books_db.append(book)
        next_book_id += 1
        
        return {
            'status': 'success',
            'message': '图书创建成功',
            'book': book
        }, 201

class BookResource(Resource):
    """单个图书资源"""
    
    def get(self, book_id):
        """获取指定图书"""
        book = next((b for b in books_db if b['id'] == book_id), None)
        if not book:
            abort(404, message=f'图书ID {book_id} 不存在')
        
        return {
            'status': 'success',
            'book': book
        }
    
    def put(self, book_id):
        """更新图书信息"""
        book = next((b for b in books_db if b['id'] == book_id), None)
        if not book:
            abort(404, message=f'图书ID {book_id} 不存在')
        
        args = book_parser.parse_args()
        
        # 更新图书信息
        book.update({
            'title': args['title'],
            'author': args['author'],
            'year': args.get('year'),
            'price': args.get('price')
        })
        
        return {
            'status': 'success',
            'message': '图书更新成功',
            'book': book
        }
    
    def delete(self, book_id):
        """删除图书"""
        global books_db
        
        book = next((b for b in books_db if b['id'] == book_id), None)
        if not book:
            abort(404, message=f'图书ID {book_id} 不存在')
        
        books_db = [b for b in books_db if b['id'] != book_id]
        
        return {
            'status': 'success',
            'message': '图书删除成功'
        }

class BookSearchResource(Resource):
    """图书搜索资源"""
    
    def get(self):
        """搜索图书"""
        title = request.args.get('title', '').lower()
        author = request.args.get('author', '').lower()
        
        filtered_books = books_db
        
        if title:
            filtered_books = [b for b in filtered_books if title in b['title'].lower()]
        
        if author:
            filtered_books = [b for b in filtered_books if author in b['author'].lower()]
        
        return {
            'status': 'success',
            'count': len(filtered_books),
            'books': filtered_books
        }

# 注册路由
api.add_resource(BookListResource, '/api/books')
api.add_resource(BookResource, '/api/books/<int:book_id>')
api.add_resource(BookSearchResource, '/api/books/search')

@app.route('/api')
def api_documentation():
    """API文档"""
    return jsonify({
        'message': '图书管理API',
        'endpoints': {
            'GET /api/books': '获取所有图书',
            'POST /api/books': '创建新图书',
            'GET /api/books/<id>': '获取指定图书',
            'PUT /api/books/<id>': '更新图书信息',
            'DELETE /api/books/<id>': '删除图书',
            'GET /api/books/search': '搜索图书'
        }
    })

# 运行RESTful API
if __name__ == '__main__':
    app.run(debug=True, port=5003)

练习5:数据库集成

# 挑战练习:任务管理Web应用
from flask import Flask, render_template, request, redirect, url_for, flash, session
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime
import os
from functools import wraps

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todo.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'todo-app-secret-key'

db = SQLAlchemy(app)

class User(db.Model):
    """用户模型"""
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password_hash = db.Column(db.String(128))
    created_at = db.Column(db.DateTime, default=datetime.now)
    
    # 关系
    tasks = db.relationship('Task', backref='user', lazy=True, cascade='all, delete-orphan')
    
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)
    
    def check_password(self, password):
        return check_password_hash(self.password_hash, password)
    
    def __repr__(self):
        return f'<User {self.username}>'

class Task(db.Model):
    """任务模型"""
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    description = db.Column(db.Text)
    priority = db.Column(db.Integer, default=1)  # 1-低, 2-中, 3-高
    completed = db.Column(db.Boolean, default=False)
    due_date = db.Column(db.Date)
    created_at = db.Column(db.DateTime, default=datetime.now)
    updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
    
    # 外键
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    
    def __repr__(self):
        return f'<Task {self.title}>'

def login_required(f):
    """登录装饰器"""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if 'user_id' not in session:
            flash('请先登录', 'error')
            return redirect(url_for('login'))
        return f(*args, **kwargs)
    return decorated_function

@app.route('/')
def index():
    """首页"""
    if 'user_id' in session:
        return redirect(url_for('dashboard'))
    return render_template('todo_index.html')

@app.route('/register', methods=['GET', 'POST'])
def register():
    """用户注册"""
    if 'user_id' in session:
        return redirect(url_for('dashboard'))
    
    if request.method == 'POST':
        username = request.form.get('username', '').strip()
        email = request.form.get('email', '').strip()
        password = request.form.get('password', '')
        confirm_password = request.form.get('confirm_password', '')
        
        # 验证输入
        if not username or not email or not password:
            flash('所有字段都是必填的', 'error')
            return render_template('todo_register.html')
        
        if password != confirm_password:
            flash('密码确认不匹配', 'error')
            return render_template('todo_register.html')
        
        if User.query.filter_by(username=username).first():
            flash('用户名已存在', 'error')
            return render_template('todo_register.html')
        
        if User.query.filter_by(email=email).first():
            flash('邮箱已存在', 'error')
            return render_template('todo_register.html')
        
        # 创建用户
        user = User(username=username, email=email)
        user.set_password(password)
        
        db.session.add(user)
        db.session.commit()
        
        flash('注册成功!请登录', 'success')
        return redirect(url_for('login'))
    
    return render_template('todo_register.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    """用户登录"""
    if 'user_id' in session:
        return redirect(url_for('dashboard'))
    
    if request.method == 'POST':
        username = request.form.get('username', '').strip()
        password = request.form.get('password', '')
        
        user = User.query.filter_by(username=username).first()
        
        if user and user.check_password(password):
            session['user_id'] = user.id
            session['username'] = user.username
            flash(f'欢迎回来,{user.username}!', 'success')
            return redirect(url_for('dashboard'))
        else:
            flash('用户名或密码错误', 'error')
    
    return render_template('todo_login.html')

@app.route('/logout')
def logout():
    """用户登出"""
    session.clear()
    flash('您已成功登出', 'success')
    return redirect(url_for('index'))

@app.route('/dashboard')
@login_required
def dashboard():
    """任务仪表板"""
    user_id = session['user_id']
    
    # 获取用户的任务
    tasks = Task.query.filter_by(user_id=user_id).order_by(
        Task.priority.desc(), Task.created_at.desc()
    ).all()
    
    # 统计信息
    total_tasks = len(tasks)
    completed_tasks = len([t for t in tasks if t.completed])
    pending_tasks = total_tasks - completed_tasks
    
    # 按优先级分组
    high_priority = [t for t in tasks if t.priority == 3 and not t.completed]
    medium_priority = [t for t in tasks if t.priority == 2 and not t.completed]
    low_priority = [t for t in tasks if t.priority == 1 and not t.completed]
    
    return render_template('todo_dashboard.html',
                         tasks=tasks,
                         total_tasks=total_tasks,
                         completed_tasks=completed_tasks,
                         pending_tasks=pending_tasks,
                         high_priority=high_priority,
                         medium_priority=medium_priority,
                         low_priority=low_priority)

@app.route('/tasks/add', methods=['GET', 'POST'])
@login_required
def add_task():
    """添加任务"""
    if request.method == 'POST':
        title = request.form.get('title', '').strip()
        description = request.form.get('description', '').strip()
        priority = int(request.form.get('priority', 1))
        due_date_str = request.form.get('due_date', '')
        
        if not title:
            flash('任务标题是必填的', 'error')
            return render_template('todo_add_task.html')
        
        # 处理截止日期
        due_date = None
        if due_date_str:
            try:
                due_date = datetime.strptime(due_date_str, '%Y-%m-%d').date()
            except ValueError:
                flash('日期格式错误', 'error')
                return render_template('todo_add_task.html')
        
        # 创建任务
        task = Task(
            title=title,
            description=description,
            priority=priority,
            due_date=due_date,
            user_id=session['user_id']
        )
        
        db.session.add(task)
        db.session.commit()
        
        flash('任务添加成功!', 'success')
        return redirect(url_for('dashboard'))
    
    return render_template('todo_add_task.html')

@app.route('/tasks/<int:task_id>/edit', methods=['GET', 'POST'])
@login_required
def edit_task(task_id):
    """编辑任务"""
    task = Task.query.get_or_404(task_id)
    
    # 检查任务是否属于当前用户
    if task.user_id != session['user_id']:
        flash('无权访问此任务', 'error')
        return redirect(url_for('dashboard'))
    
    if request.method == 'POST':
        task.title = request.form.get('title', '').strip()
        task.description = request.form.get('description', '').strip()
        task.priority = int(request.form.get('priority', 1))
        due_date_str = request.form.get('due_date', '')
        
        if not task.title:
            flash('任务标题是必填的', 'error')
            return render_template('todo_edit_task.html', task=task)
        
        # 处理截止日期
        task.due_date = None
        if due_date_str:
            try:
                task.due_date = datetime.strptime(due_date_str, '%Y-%m-%d').date()
            except ValueError:
                flash('日期格式错误', 'error')
                return render_template('todo_edit_task.html', task=task)
        
        db.session.commit()
        flash('任务更新成功!', 'success')
        return redirect(url_for('dashboard'))
    
    return render_template('todo_edit_task.html', task=task)

@app.route('/tasks/<int:task_id>/toggle')
@login_required
def toggle_task(task_id):
    """切换任务完成状态"""
    task = Task.query.get_or_404(task_id)
    
    if task.user_id != session['user_id']:
        flash('无权访问此任务', 'error')
        return redirect(url_for('dashboard'))
    
    task.completed = not task.completed
    db.session.commit()
    
    status = '完成' if task.completed else '重新打开'
    flash(f'任务已标记为{status}!', 'success')
    return redirect(url_for('dashboard'))

@app.route('/tasks/<int:task_id>/delete')
@login_required
def delete_task(task_id):
    """删除任务"""
    task = Task.query.get_or_404(task_id)
    
    if task.user_id != session['user_id']:
        flash('无权访问此任务', 'error')
        return redirect(url_for('dashboard'))
    
    db.session.delete(task)
    db.session.commit()
    
    flash('任务删除成功!', 'success')
    return redirect(url_for('dashboard'))

def init_todo_database():
    """初始化数据库"""
    with app.app_context():
        db.create_all()

def create_todo_templates():
    """创建任务管理应用的模板"""
    os.makedirs('templates', exist_ok=True)
    
    templates = {
        'todo_index.html': '''
        {% extends "base.html" %}
        
        {% block title %}任务管理应用{% endblock %}
        
        {% block content %}
        <div style="text-align: center; margin-top: 100px;">
            <h1>任务管理应用</h1>
            <p>高效管理您的日常任务</p>
            
            <div style="margin-top: 50px;">
                <a href="{{ url_for('login') }}" style="background: #007bff; color: white; padding: 15px 30px; text-decoration: none; border-radius: 5px; margin-right: 20px;">
                    登录
                </a>
                <a href="{{ url_for('register') }}" style="background: #28a745; color: white; padding: 15px 30px; text-decoration: none; border-radius: 5px;">
                    注册
                </a>
            </div>
            
            <div style="margin-top: 50px; max-width: 600px; margin-left: auto; margin-right: auto;">
                <h3>功能特性</h3>
                <ul style="text-align: left; display: inline-block;">
                    <li>用户注册和登录</li>
                    <li>创建、编辑、删除任务</li>
                    <li>任务优先级管理</li>
                    <li>任务完成状态跟踪</li>
                    <li>截止日期设置</li>
                </ul>
            </div>
        </div>
        {% endblock %}
        ''',
        
        'todo_register.html': '''
        {% extends "base.html" %}
        
        {% block title %}用户注册{% endblock %}
        
        {% block content %}
        <div style="max-width: 400px; margin: 0 auto;">
            <h1>用户注册</h1>
            
            <form method="POST">
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px;">用户名:</label>
                    <input type="text" name="username" required style="width: 100%; padding: 8px;">
                </div>
                
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px;">邮箱:</label>
                    <input type="email" name="email" required style="width: 100%; padding: 8px;">
                </div>
                
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px;">密码:</label>
                    <input type="password" name="password" required style="width: 100%; padding: 8px;">
                </div>
                
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 5px;">确认密码:</label>
                    <input type="password" name="confirm_password" required style="width: 100%; padding: 8px;">
                </div>
                
                <button type="submit" style="width: 100%; background: #28a745; color: white; padding: 10px; border: none; border-radius: 5px;">注册</button>
            </form>
            
            <p style="margin-top: 20px; text-align: center;">
                已有账号? <a href="{{ url_for('login') }}">点击登录</a>
            </p>
        </div>
        {% endblock %}
        ''',
        
        # 其他模板类似,由于篇幅限制这里省略...
        # 实际项目中需要创建完整的模板文件
    }
    
    for filename, content in templates.items():
        with open(f'templates/{filename}', 'w', encoding='utf-8') as f:
            f.write(content)

# 运行任务管理应用
if __name__ == '__main__':
    create_todo_templates()
    init_todo_database()
    app.run(debug=True, port=5005)

学习提示:

  1. Flask基础:路由、视图函数、模板渲染
  2. 表单处理:GET/POST请求、数据验证、CSRF保护
  3. 数据库集成:SQLAlchemy ORM、模型定义、关系管理
  4. 用户认证:注册、登录、会话管理、密码加密
  5. RESTful API:资源设计、HTTP方法、状态码
  6. 模板继承:base模板、块继承、代码复用
  7. 错误处理:404页面、表单验证、异常处理
  8. 安全考虑:密码哈希、SQL注入防护、XSS防护

明天我们将学习数据分析和可视化!坚持练习,你的Web开发技能会越来越强!

Tags:

最近发表
标签列表