网站首页 > 技术文章 正文
今日练习主题: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)
学习提示:
- Flask基础:路由、视图函数、模板渲染
- 表单处理:GET/POST请求、数据验证、CSRF保护
- 数据库集成:SQLAlchemy ORM、模型定义、关系管理
- 用户认证:注册、登录、会话管理、密码加密
- RESTful API:资源设计、HTTP方法、状态码
- 模板继承:base模板、块继承、代码复用
- 错误处理:404页面、表单验证、异常处理
- 安全考虑:密码哈希、SQL注入防护、XSS防护
明天我们将学习数据分析和可视化!坚持练习,你的Web开发技能会越来越强!
猜你喜欢
- 2025-10-14 SpringBoot Jar包冲突在线检测_springboot jar war
- 2025-10-14 前端笔记:HTML output标签介绍及用法
- 2025-10-14 前端切图css高级阴影用法drop-shadow
- 2025-10-14 CSS 颜色体系详解_css颜色写法
- 2025-10-14 仅使用一个 DIV 配合 CSS 实现饼状图
- 2025-10-14 Python——Html(表格, , ,、表单 、自定义标签 和)
- 2025-10-14 精通Vue(14):组件状态同步的sync原理
- 2024-08-09 网页制作里的边框border常见用法(包含圆角)【210】
- 2024-08-09 总结Css 常用的操作(css基本操作)
- 2024-08-09 css实现渐变色圆角边框(css渐变背景色)
- 最近发表
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (87)
- localstorage.removeitem (77)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)