You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask烘焙博客删除带评分食谱报rating.recipe_id非空约束错误如何解决

问题解决方案

问题根因

  • 级联删除规则配置错误:当前你在Rating模型中定义了recipe_id字段不可为空(nullable=False),但Recipe和Rating的关联关系没有配置正确的级联规则。当你删除存在关联评分的食谱时,SQLAlchemy默认会尝试将对应评分的recipe_id设置为NULL,直接违反了非空约束,因此触发报错。
  • 路由重复定义冲突:你定义了两个绑定到/recipes/<int:recipe_id>路径的视图函数,Flask会按代码书写顺序优先匹配先定义的视图,导致评分提交的请求根本不会走到评分逻辑对应的视图函数中。

修复方案

方案1:删除食谱时自动删除关联评分(符合通用业务逻辑)

  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}')>"
  1. 修复路由冲突,将评分提交的路由调整为独立路径:
# 将原评分路由的路径改为/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
                           )
  1. 对应修改前端模板中评分表单的提交地址为/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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 21:57:04