
本文讲解如何在 flask 应用中正确校验用户提交的酒店名称是否已存在于 mysql 表中,避免重复插入;重点修正原始代码中循环逻辑错误、比较操作符误用及数据库资源管理问题。
本文讲解如何在 flask 应用中正确校验用户提交的酒店名称是否已存在于 mysql 表中,避免重复插入;重点修正原始代码中循环逻辑错误、比较操作符误用及数据库资源管理问题。
在 Flask 开发中,常见需求是:接收表单数据(如酒店名称),先检查数据库中是否已存在同名记录,仅当不存在时才执行 INSERT。原始代码存在多个关键问题——使用 while 循环逐条读取并错误地用 != 比较单个元组与字符串、未及时退出循环、数据库游标未统一管理、且未处理空值或 SQL 注入风险。
以下是优化后的完整实现方案:
✅ 正确做法:一次性获取 + 成员判断 + 安全校验
@app.route('/createHotel', methods=['GET', 'POST'])
def createHotel():
if "loggedin" not in session or session['role'] != 'admin':
return redirect(url_for("home"))
user = session["username"]
userid = session["userid"]
# 1. 获取所有已有酒店名称(只查 name 字段,提升效率)
cur = mysql.connection.cursor()
cur.execute('SELECT name FROM hotels')
existing_names = [row[0] for row in cur.fetchall()] # 提取为纯字符串列表,如 ['Hilton', 'Marriott']
cur.close() # 立即释放游标,避免阻塞
if request.method == "POST":
# 2. 提取表单字段(建议使用 get() 防 KeyError)
name = request.form.get('name', '').strip()
image = request.form.get('image', '')
city = request.form.get('city', '')
features = request.form.get('features', '')
RoomCount = request.form.get('RoomCount', '0')
peakSeasonRate = request.form.get('peakSeasonRate', '0')
OffPeakSeasonRate = request.form.get('OffPeakSeasonRate', '0')
# 3. 关键校验:非空 + 不存在于数据库
if name and name not in existing_names:
try:
cur = mysql.connection.cursor()
cur.execute(
'INSERT INTO hotels (image, name, city, features, RoomCount, peakSeasonRate, OffPeakSeasonRate) '
'VALUES (%s, %s, %s, %s, %s, %s, %s)',
(image, name, city, features, RoomCount, peakSeasonRate, OffPeakSeasonRate)
)
mysql.connection.commit()
flash('酒店添加成功', 'success')
return redirect(url_for('hotelRecord'))
except Exception as e:
mysql.connection.rollback()
flash(f'数据库写入失败:{str(e)}', 'error')
finally:
if 'cur' in locals():
cur.close()
else:
flash('酒店名称已存在或不能为空', 'warning')
return render_template("createHotel.html", user=user, userid=userid)
⚠️ 注意事项与最佳实践
-
不要用
!=比较元组与字符串:fetchall()返回的是元组列表(如[('Hilton',), ('Marriott',)]),直接name != names[i]实际是在比'Hilton' != ('Hilton',),恒为True。 -
避免循环中混入请求处理逻辑:原代码将
if request.method == "POST"错误地嵌套在while内部,导致每次循环都重复处理表单,极易引发多次插入或逻辑错乱。 -
使用
in/not in判断成员关系:配合列表推导式existing_names = [row[0] for row in h],语义清晰、性能可控(适用于中小数据量)。 -
空值防御:务必用
.strip()和if name校验,防止插入空白名称。 -
异常处理与事务回滚:INSERT 可能因唯一约束等失败,需
try...except...rollback保障数据一致性。 -
游标生命周期管理:查询后立即
close();插入前重新获取游标,并在finally块中确保关闭。
? 进阶建议(大数据量场景)
若酒店数量达万级,建议改用 数据库原生去重机制:
# 使用 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE(需 name 字段设 UNIQUE 索引)
cur.execute(
'INSERT IGNORE INTO hotels (image, name, city, ...) VALUES (%s, %s, %s, ...)',
(image, name, city, ...)
)
配合数据库层面的 UNIQUE(name) 约束,既高效又原子,彻底规避应用层竞态条件。
通过以上重构,你的表单提交逻辑将变得健壮、可维护且符合 Web 开发最佳实践。











