Flask中SelectMultipleField写入SQLAlchemy多对多数据提交异常
问题原因
提交选了分类的表单时停留在表单页、不跳转,核心是form.validate_on_submit()返回了False,请求根本没进入入库逻辑。
验证失败的直接原因是SelectMultipleField的choices配置不符合WTForms要求:你直接传入了Category模型对象列表,而Select类字段的choices必须是(字段提交值, 页面显示文本)格式的二元元组,字段校验时无法将提交的参数和你配置的选项匹配,直接判定为非法值,阻断提交流程。未选择分类时字段为空,不触发选项校验,所以可以正常提交跳转。
除此之外代码还有两个隐藏bug:
- 表单类定义时直接执行
Category.query.all()加载选项,会在类加载阶段触发数据库查询,若此时数据库未初始化会直接报错 - 视图中每次提交都新建Category对象,和Category模型title字段的
unique=True约束冲突,只要选已存在的分类,数据库提交阶段就会报唯一键重复错误
修复方案
1. 修改表单定义
去掉定义时写死的choices,增加coerce参数把提交的分类id转为整型:
class EntryForm(FlaskForm): title = StringField('Title', validators=[DataRequired()]) content = TextAreaField('Content') categories = SelectMultipleField('Category', coerce=int) submit = SubmitField('Add')
2. 修改视图逻辑
在视图中动态加载分类选项,关联已有分类而非新建,简化入库后id的获取逻辑:
@app.route('/add_entry', methods=['GET', 'POST']) def add_entry(): form = EntryForm() # 动态加载分类选项,格式符合WTForms要求 form.categories.choices = [(cat.id, cat.title) for cat in Category.query.all()] if form.validate_on_submit(): # 直接从表单对象取校验通过的数据,无需手动读取request.form entry = Entry( title=form.title.data, content=form.content.data ) # 查询已存在的选中分类,建立多对多关联,不要重复新建分类 entry.categories = Category.query.filter(Category.id.in_(form.categories.data)).all() db.session.add(entry) db.session.commit() session['entry_id'] = entry.id return redirect(f'entries/{entry.id}') # 调试时可打开下面这行打印,查看具体的表单验证错误信息 # print(form.errors) return render_template('add_entry.html', form=form)
不需要修改模型和模板代码,改完这两处后,选中分类提交就可以正常入库跳转,也不会出现分类重复创建的问题。
内容的提问来源于stack exchange,提问作者Lual Jalilov
相关产品推荐
相关产品推荐

