Flask烘焙博客删除带评分食谱报rating.recipe_id非空约束错误如何解决
问题解决方案
问题根因
- 级联删除规则配置错误:当前你在Rating模型中定义了
recipe_id字段不可为空(nullable=False),但Recipe和Rating的关联关系没有配置正确的级联规则。当你删除存在关联评分的食谱时,SQLAlchemy默认会尝试将对应评分的recipe_id设置为NULL,直接违反了非空约束,因此触发报错。 - 路由重复定义冲突:你定义了两个绑定到
/recipes/<int:recipe_id>路径的视图函数,Flask会按代码书写顺序优先匹配先定义的视图,导致评分提交的请求根本不会走到评分逻辑对应的视图函数中。
修复方案
方案1:删除食谱时自动删除关联评分(符合通用业务逻辑)
- 修改Rating模型中的关系配置,增加级联删除参数:
class Rating(db.Model): id = db.Column(db.Integer, primary_key=True, nullable=False) rating = db.Column(db.Integer, nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) user = db.relationship(User, backref=db.backref('ratings', lazy=True)) recipe_id = db.Column(db.Integer, db.ForeignKey('recipe.id'), nullable=False) # 仅修改下方这行,增加cascade参数 recipe = db.relationship(Recipe, backref=db.backref('ratings', lazy=True, cascade="all, delete-orphan")) def __repr__(self): return f"<Rating (id='{self.id}', rating='{self.rating}', user_id='{self.user_id}', recipe_id='{self.recipe_id}')>"
- 修复路由冲突,将评分提交的路由调整为独立路径:
# 将原评分路由的路径改为/recipes/<int:recipe_id>/rate,避免和食谱详情路由冲突 @app.route("/recipes/<int:recipe_id>/rate", methods=['GET', 'POST']) @login_required def rating(recipe_id): # 原有逻辑保持不变 current_recipe = Recipe.query.get_or_404(recipe_id) form = RatingForm() if form.validate_on_submit(): if current_user.is_authenticated: new_rating = Rating(rating=form.rating.data, user=current_user, recipe=current_recipe) db.session.add(new_rating) try: db.session.commit() form.rating.data = "" flash('评分提交成功', 'success') except Exception as e: db.session.rollback() app.logger.critical(f'评分提交出错: {new_rating}') app.logger.exception(e) flash('评分提交失败,请稍后重试', 'danger') else: flash('你需要登录后才能对食谱评分', 'danger') avg_rating = db.session.query(func.avg(Rating.rating).label('average')).filter(Rating.recipe_id == recipe_id).first()[0] return render_template('recipe_page.html', title=current_recipe.title, avg_rating=avg_rating, post=current_recipe, form=form )
- 对应修改前端模板中评分表单的提交地址为
/recipes/{{ post.id }}/rate即可。
方案2:禁止删除存在评分的食谱
如果业务要求存在评分的食谱不允许删除,不需要自动删除关联评分,可以直接在删除逻辑中增加判断:
@app.route("/recipes/<int:recipe_id>/delete", methods=['POST']) @login_required def delete_recipe(recipe_id): recipe = Recipe.query.get_or_404(recipe_id) if recipe.user != current_user: abort(403) # 增加判断:如果有关联评分则提示无法删除 if len(recipe.ratings) > 0: flash('该食谱存在用户评分,无法删除', 'danger') return redirect(url_for('recipe', recipe_id=recipe_id)) db.session.delete(recipe) db.session.commit() flash('食谱已删除', 'success') return redirect(url_for('home'))
使用该方案也需要同步按方案1的步骤修复路由冲突问题。
内容的提问来源于stack exchange,提问作者elephant176
相关产品推荐
相关产品推荐

