Flask中WTForms SelectField取值插入MySQL时SQL语法错误问题
解决Flask+WTForms中SelectField获取表名导致SQL语法错误的问题
嘿,这个问题我之前踩过坑!咱们先拆解一下错误原因:
你用参数化查询的%s占位符来处理表名,但MySQL的参数化机制是专门用来处理字段值的,不是表名、列名这种数据库标识符。当你把main_dishes传进去当表名时,它会被自动套上单引号,最终SQL变成了:
INSERT INTO 'main_dishes'(name,ingredients,price) VALUES('Salmon', ' duude,frv ', '35')
而SQL语法里,表名不能用单引号包裹(要用反引号或者直接写),这就导致了1064语法错误。
解决方案:白名单校验+表名字符串格式化
直接拼接表名有SQL注入风险,所以咱们先做白名单校验,确保用户选择的表名是你预设的合法值,再用字符串格式化把表名拼进SQL,字段值依然用参数化查询保证安全。
修改后的代码如下:
class MenuForm(Form): menu_type = SelectField('Menu Type', [validators.DataRequired()], choices=[('appetizers','Appetizer'),('main_dishes','Main Dish'),('desserts','Dessert'),('drinks','Drinks')], coerce=str) name = StringField('Name', [validators.Length(min=1, max=2000)]) ingredients = TextAreaField('Ingredients', [validators.Length(min=10)]) price = DecimalField('Price (Manat)', [validators.DataRequired()]) @app.route('/add_menu', methods=['GET','POST']) @is_logged_in def add_menu(): form = MenuForm(request.form) # 定义合法表名白名单,和SelectField的选项一一对应 valid_menu_tables = {'appetizers', 'main_dishes', 'desserts', 'drinks'} if request.method == 'POST' and form.validate(): menu_type = form.menu_type.data # 第一步:校验表名是否在白名单内,防止SQL注入 if menu_type not in valid_menu_tables: flash('Invalid menu category!', 'danger') return redirect(url_for('add_menu')) name = form.name.data ingredients = form.ingredients.data price = form.price.data # 创建游标 cur = mysql.connection.cursor() # 表名用f-string格式化(加反引号避免特殊字符冲突),值用参数化占位符 cur.execute(f"INSERT INTO `{menu_type}`(name, ingredients, price) VALUES(%s, %s, %s)", (name, ingredients, price)) # 提交并关闭连接 mysql.connection.commit() cur.close() flash('Menu is Added', 'success') return redirect(url_for('dashboard')) return render_template('add_menu.html', form=form)
关键要点说明:
- 白名单校验:虽然用户是通过SelectField选择的选项,但恶意请求可能篡改
menu_type的值,白名单能直接拦截非法输入,彻底杜绝SQL注入风险。 - 表名用反引号包裹:如果你的表名包含特殊字符或MySQL关键字,反引号可以避免语法错误,即使没有特殊字符,加上也更稳妥。
- 字段值仍用参数化:菜品名称、配料这些用户输入的内容,还是用
%s占位符,既防止SQL注入,又能正确处理包含单引号等特殊字符的内容。
内容的提问来源于stack exchange,提问作者BAT
相关产品推荐
相关产品推荐

