
本文介绍 Flask 应用中避免重复插入的关键实践:通过一次性查询数据库获取所有酒店名称列表,再使用 not in 判断表单提交的名称是否已存在,从而实现安全、高效的唯一性校验。
本文介绍 flask 应用中避免重复插入的关键实践:通过一次性查询数据库获取所有酒店名称列表,再使用 `not in` 判断表单提交的名称是否已存在,从而实现安全、高效的唯一性校验。
在 Flask 开发中,新手常误用循环逐条比对表单数据与数据库记录,不仅逻辑混乱、性能低下,还极易因变量作用域或条件嵌套错误导致校验失效(如原代码中 if name != names 实际比较的是字符串与元组,永远为 True)。正确的做法是先批量获取、后集中判断。
✅ 正确实现步骤
-
一次性查询全部名称:使用
fetchall()获取所有酒店名,并转换为 Python 列表; -
提取纯名称字符串:注意
fetchall()返回的是元组列表(如[('Grand Hotel',), ('Sunrise Inn',)]),需用列表推导式提取首项; -
使用
not in安全校验:确保新名称不在已有名称集合中,且非空; - 分离查询与写入逻辑:避免在循环内执行插入操作,防止多次提交或状态错乱。
以下是优化后的完整路由代码:
@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()] # 提取字符串列表:['Grand Hotel', 'Sunrise Inn']
cur.close()
if request.method == "POST":
# 2. 提取表单字段(建议使用 get() 避免 KeyError)
name = request.form.get('name', '').strip()
image = request.form.get('image', '').strip()
city = request.form.get('city', '').strip()
features = request.form.get('features', '').strip()
RoomCount = request.form.get('RoomCount', '0').strip()
peakSeasonRate = request.form.get('peakSeasonRate', '0').strip()
OffPeakSeasonRate = request.form.get('OffPeakSeasonRate', '0').strip()
# 3. 校验:名称非空 + 不存在于数据库
if name and name not in existing_names:
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()
cur.close()
flash('酒店添加成功', 'success')
return redirect(url_for('hotelRecord'))
else:
flash('该酒店名称已存在,请更换', 'warning')
return render_template("createHotel.html", user=user, userid=userid)
⚠️ 关键注意事项
-
SQL 注入防护:始终使用参数化查询(
%s占位符),切勿拼接字符串; -
空值防御:用
.get(key, default)和.strip()处理可能为空或仅含空格的输入; -
数据库连接管理:
cursor()应在需要时创建,使用后及时close(),避免连接泄漏; -
性能提示:若酒店数量极大(>10,000 条),建议改用数据库层唯一约束(
UNIQUE INDEX ON name)配合INSERT IGNORE或ON DUPLICATE KEY UPDATE,更高效可靠; -
调试技巧:开发阶段可加
print(f"Existing: {existing_names}, Submitted: '{name}'")快速定位逻辑问题。
通过以上重构,代码逻辑清晰、健壮性强,既符合 Web 开发最佳实践,也为你后续扩展(如支持模糊匹配、多字段联合去重)打下坚实基础。











