当前位置: 首页 > news >正文

Flask电影投票系统全解析

电影投票案列
  • 项目结构说明:

movie-voting-app/
├── app.py
├── models.py
├── templates/
│   ├── index.html
│   └── add_movie.html
└── static/└── style.css

1. 导入对应的依赖包,并配置数据库链接
from flask import Flask, render_template, request, redirect, url_for, flash
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
import os
from datetime import datetime
​
# 创建应用
app = Flask(__name__)
​
# 配置
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:123456@127.0.0.1/youdb'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'pgijeaowir12734y#$^^lefmkng'
​
# 初始化数据库
db = SQLAlchemy(app)
migrate = Migrate(app, db)
​
if __name__ == '__main__':with app.app_context():app.run(debug=True)
2. 创建电影模型,并添加默认数据
# 电影模型
class Movie(db.Model):id = db.Column(db.Integer, primary_key=True)title = db.Column(db.String(100), nullable=False)year = db.Column(db.Integer, nullable=False)votes = db.Column(db.Integer, default=0)
​def __repr__(self):return f'<Movie {self.title}>'# 添加数据
if __name__ == '__main__':with app.app_context():db.create_all()
​# 添加初始数据(如果不存在)if Movie.query.count() == 0:movies = [Movie(title="盗梦空间", year=2010, votes=0),Movie(title="肖申克的救赎", year=1994, votes=0),Movie(title="泰坦尼克号", year=1997, votes=0),Movie(title="阿甘正传", year=1994, votes=0),]db.session.add_all(movies)db.session.commit()
​app.run(debug=True)

3. 添加电影页面路由,显示初始化数据
  • 准备路由,查询数据并在页面中显示

@app.route('/index', methods=['GET'])
def index():# 获取所有电影movies = Movie.query.all()return render_template('index.html',current_year=datetime.now().year,movies=movies)
  • 展示首页数据

<!DOCTYPE html>
<html>
<head><title>电影投票系统</title><link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body><div class="container"><header><h1>🎬 电影投票系统</h1><p class="subtitle">为您喜欢的电影投票</p></header>
​{#        闪现消息#}{% with messages = get_flashed_messages() %}{% if messages %}<div class="messages">{% for message in messages %}<div class="message">{{ message }}</div>{% endfor %}</div>{% endif %}{% endwith %}
​<div class="controls"><a href="{{ url_for('add_movie_page') }}" class="button">➕ 添加电影</a><form action="{{ url_for('reset_votes') }}" method="post" class="inline-form"><button type="submit" class="button warning">🔄 重置票数</button></form></div>
​<div class="movie-list">{% if movies %}<table><thead><tr><th>电影名称</th><th>年份</th><th>票数</th><th>操作</th></tr></thead><tbody>{% for movie in movies %}<tr><td class="movie-title">{{ movie.title }}</td><td class="movie-year">{{ movie.year }}</td><td class="movie-votes">{{ movie.votes }}</td><td class="movie-actions"><form action="{{ url_for('vote_movie', movie_id=movie.id) }}" method="post"><button type="submit" class="small-button">👍 投票</button></form><form action="{{ url_for('delete_movie', movie_id=movie.id) }}" method="post" class="inline-form"><button type="submit" class="small-button danger" onclick="return confirm('确定删除这部电影吗?')">🗑️ 删除</button></form></td></tr>{% endfor %}</tbody></table>{% else %}<div class="no-movies"><p>还没有电影,请添加一些!</p><a href="{{ url_for('add_movie_page') }}" class="button">添加第一部电影</a></div>{% endif %}</div>
​<footer><p>© {{ current_year }} 电影投票系统 | hui函数出品</p></footer></div>
</body>
</html>
4. 投票功能
  • 接收传递过来的id值,得到数据对象,给votes字段数据+1,并提交

  • 使用flash闪现消息,发送操作提示

  • 重定向到首页

@app.route('/vote/<int:movie_id>', methods=['POST'])
def vote_movie(movie_id):movie = Movie.query.get(movie_id)if movie:movie.votes += 1db.session.commit()flash(f'已为 {movie.title} 投票成功!')return redirect(url_for('index'))
5. 删除功能
  • 获取传递过来的id,执行删除操作

  • 使用flash闪现消息,发送操作提示

  • 重定向到首页

@app.route('/delete/<int:movie_id>', methods=['POST'])
def delete_movie(movie_id):movie = Movie.query.get(movie_id)if movie:db.session.delete(movie)db.session.commit()flash(f'已删除电影: {movie.title}')return redirect(url_for('index'))

6. 重置票数
  • 使用update方法直接修改票数votes为0

@app.route('/reset', methods=['POST'])
def reset_votes():Movie.query.update({Movie.votes: 0})db.session.commit()flash('所有票数已重置!')return redirect(url_for('index'))
7. 添加数据
  • 准备一个显示添加页面的路由

@app.route('/add', methods=['GET'])
def add_movie_page():return render_template('add_movie.html', current_year=datetime.now().year)
  • 显示页面

<!DOCTYPE html>
<html>
<head><title>添加电影</title><link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body><div class="container"><header><h1>➕ 添加新电影</h1><a href="{{ url_for('index') }}" class="button back">← 返回首页</a></header>
​{#    闪现消息#}{% with messages = get_flashed_messages() %}{% if messages %}<div class="messages">{% for message in messages %}<div class="message">{{ message }}</div>{% endfor %}</div>{% endif %}{% endwith %}
​<div class="form-container"><form action="{{ url_for('add_movie') }}" method="post"><div class="form-group"><label for="title">电影名称</label><input type="text" id="title" name="title" required></div>
​<div class="form-group"><label for="year">发行年份</label><input type="number" id="year" name="year"min="1900" max="{{ current_year }}"value="{{ current_year }}" required></div>
​<div class="form-actions"><button type="submit" class="button">添加电影</button><button type="reset" class="button secondary">重置</button></div></form></div>
​<footer><p>© {{ current_year }} 电影投票系统 | hui函数</p></footer></div>
</body>
</html>
  • 设置添加电影的路由

@app.route('/add', methods=['POST'])
def add_movie():title = request.form.get('title', '').strip()year = request.form.get('year', '')
​if not title or not year:flash('电影名称和年份必须填写')return redirect(url_for('add_movie_page'))
​try:year = int(year)if year < 1900 or year > datetime.now().year:flash('年份不合法')return redirect(url_for('add_movie_page'))except ValueError:flash('请输入数字年份')return redirect(url_for('add_movie_page'))
​existing = Movie.query.filter_by(title=title).first()if existing:flash(f'电影 "{title}" 已存在!')return redirect(url_for('add_movie_page'))
​movie = Movie(title=title, year=year)db.session.add(movie)db.session.commit()
​flash(f'电影 "{title}" 添加成功!')return redirect(url_for('index'))

公共样式
  • 在static中创建一个style.css的文件

/* 基础样式 */
* {margin: 0;padding: 0;box-sizing: border-box;font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
​
body {background-color: #f5f7fa;color: #333;line-height: 1.6;padding: 20px;
}
​
.container {max-width: 1000px;margin: 0 auto;padding: 20px;
}
​
header {text-align: center;margin-bottom: 30px;padding-bottom: 20px;border-bottom: 1px solid #eaeaea;
}
​
h1 {color: #2c3e50;font-size: 2.5rem;margin-bottom: 10px;
}
​
.subtitle {color: #7f8c8d;font-size: 1.2rem;
}
​
/* 消息提示 */
.messages {margin: 20px 0;
}
​
.message {padding: 15px;margin-bottom: 10px;border-radius: 4px;background: #e3f2fd;color: #1976d2;border-left: 4px solid #1976d2;
}
​
/* 按钮样式 */
.button {display: inline-block;padding: 10px 20px;background: #3498db;color: white;text-decoration: none;border-radius: 4px;border: none;cursor: pointer;font-size: 1rem;transition: background 0.3s;
}
​
.button:hover {background: #2980b9;
}
​
.button.back {background: #95a5a6;
}
​
.button.back:hover {background: #7f8c8d;
}
​
.button.warning {background: #f39c12;
}
​
.button.warning:hover {background: #e67e22;
}
​
.button.secondary {background: #95a5a6;
}
​
.button.secondary:hover {background: #7f8c8d;
}
​
/* 表单控件 */
.controls, .form-actions {margin: 20px 0;display: flex;gap: 10px;
}
​
.inline-form {display: inline-block;
}
​
/* 电影列表 */
.movie-list {background: white;border-radius: 8px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);padding: 20px;margin: 20px 0;
}
​
table {width: 100%;border-collapse: collapse;
}
​
th, td {padding: 12px 15px;text-align: left;border-bottom: 1px solid #eee;
}
​
th {background-color: #f8f9fa;color: #34495e;font-weight: 600;
}
​
tr:hover {background-color: #f9f9f9;
}
​
.movie-title {font-weight: 600;
}
​
.movie-votes {font-weight: bold;color: #e74c3c;
}
​
.movie-actions {display: flex;gap: 8px;
}
​
.small-button {padding: 6px 12px;font-size: 0.9rem;
}
​
.small-button.danger {background: #e74c3c;
}
​
.small-button.danger:hover {background: #c0392b;
}
​
/* 添加电影表单 */
.form-container {background: white;border-radius: 8px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);padding: 30px;margin: 20px 0;
}
​
.form-group {margin-bottom: 20px;
}
​
label {display: block;margin-bottom: 8px;font-weight: 600;color: #2c3e50;
}
​
input[type="text"],
input[type="number"] {width: 100%;padding: 10px 15px;border: 1px solid #ddd;border-radius: 4px;font-size: 1rem;
}
​
input:focus {outline: none;border-color: #3498db;
}
​
/* 无电影提示 */
.no-movies {text-align: center;padding: 40px 20px;color: #7f8c8d;
}
​
/* 页脚 */
footer {text-align: center;margin-top: 40px;padding-top: 20px;border-top: 1px solid #eaeaea;color: #95a5a6;font-size: 0.9rem;
}

http://www.dtcms.com/a/344389.html

相关文章:

  • 近期https接口的联调小记
  • STM32——SPI通信+W25Q64
  • 一体化伺服电机在特种机器人(炉管爬行器)中的应用案例
  • ShimetaPi M4-R1 :OpenHarmony 开发板解析
  • Mysql事务特性及原理
  • 网络安全基础知识
  • 异步开发的三种实现方式
  • 香港券商櫃台系統跨境金融研究
  • CTFshow系列——命令执行web45-48
  • 优选算法1:双指针
  • 如何在Vscode中配置MCP服务?(包含实例:使用Github Copilot + 高德MCP查询旅游攻略)
  • 聚焦AI与绿色双碳 金士顿亮相2025深圳国际电子展
  • 【链表 - LeetCode】2. 两数相加
  • 深度学习——神经网络
  • 深度学习赋能光纤非线性光学:Nature Comms揭示噪声驱动系统的智能预测框架
  • 【openGLES】着色器语言(GLSL)
  • CAM可视化卷积神经网络
  • 开源 python 应用 开发(十一)短语音转文本
  • 安卓手机格式转换,支持PDF转Word、PDF转Excel、PDF转PPT、PDT转图片
  • 基于ERNIE 4.5的多智能体协作的自动化视频舆情分析报告生成器
  • UE5基本打光(新手向)
  • vue2整合uniapp、uviewUi小程序开发
  • Yapi中通过MongoDB修改管理员密码与新增管理员
  • PDF 转 TIFF 性能测评:IronPDF具有更快的处理速度、更少的内存
  • 携程旅游的 AI 网关落地实践
  • 爬虫基础学习-链接协议分析,熟悉相关函数
  • C++中的右值引用与通用引用:std::move与std::forward的正确使用 (Effective Modern C++ 条款25)
  • 中项-基础知识分享12-软件工程
  • 保护 PDF 格式:禁止转换为其他格式文件
  • Python第三方库IPFS-API使用详解:构建去中心化应用的完整指南