Flask路由间变量传递或会话异常:图片上传后无法正确传递p_id至view路由
嘿,我来帮你捋清楚问题出在哪——你遇到的麻烦根本不是会话的锅,核心原因是表单提交时没把p_id带过去,导致POST请求里拿不到这个参数,最终redirect到view路由时p_id变成了None,引发了后续一连串错误。
咱们一步步拆解问题:
问题根源
当你从/view/<p_id>页面点击链接跳转到/add_main_img时,p_id是通过URL的GET参数传递的(比如/add_main_img?p_id=123),这时候GET请求里request.args.get("p_id")能拿到值。但当你提交上传表单时,这是个POST请求,而你的表单里完全没携带p_id这个参数,所以此时request.args.get("p_id")会返回None——这就是为什么上传后redirect到view路由时会报BuildError,因为p_id是None,没法生成合法的URL。
你后来把p_id转成字符串,其实是把None转成了字符串"None",传到view路由后,数据库查询p_id="None"自然找不到对应记录,所以触发了IndexError(fetchall()返回空列表,直接取[0]肯定报错)。
解决方案
这里有两个靠谱的解决思路,选你觉得顺手的就行:
方案1:在表单里添加隐藏字段携带p_id
这是最直接的方法,把p_id藏在上传表单里,让POST请求一起提交:
- 修改
add_main_img.html的表单,添加隐藏字段:
{% extends "layout.html" %} {% block title %} Add main image {% endblock %} {% block main %} <h2>Upload new</h2> <form action="/add_main_img" method="post" enctype = "multipart/form-data"> <!-- 添加隐藏字段传递p_id --> <input type="hidden" name="p_id" value="{{ p_id }}"> <div> <input class="form-control mx-auto w-auto" type="file" name="file" placeholder="Add Profile Image"> </div> <input type="submit" > </form> {% endblock %}
- 修改
add_main_img路由,同时支持从GET参数和POST表单里获取p_id(用request.values.get可以同时读取GET和POST的参数):
@app.route("/add_main_img", methods=["GET", "POST"]) @login_required def add_main_img(): """Add new item to database""" # 用request.values.get同时兼容GET和POST的参数 p_id = request.values.get("p_id") # 先判断p_id是否存在,避免后续报错 if not p_id: flash("缺少必要的参数") return redirect(url_for("index")) # 替换成你的首页或合适的路由 if request.method == "GET": return render_template("add_main_img.html", p_id=p_id) # 把p_id传给模板渲染隐藏字段 else: # 原本的文件上传逻辑不变,确保p_id有效即可 if 'file' not in request.files: flash('No file part') return render_template("add_main_img.html", p_id=p_id) file = request.files['file'] if file.filename == '': flash("No image selected for upload") return redirect(request.url) if file and allowed_file(file.filename): filename = secure_filename(file.filename) file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) flash("Image succesfully uploaded") return redirect(url_for('view', p_id=p_id)) else: flash("Allowed image types are .png, .jpg, .jpeg") return redirect(request.url)
方案2:把p_id存在Session里
如果不想在表单里传参数,也可以用Session暂存p_id:
- 修改
add_main_img的GET请求逻辑,把p_id存入Session:
@app.route("/add_main_img", methods=["GET", "POST"]) @login_required def add_main_img(): """Add new item to database""" if request.method == "GET": p_id = request.args.get("p_id") if not p_id: flash("缺少必要的参数") return redirect(url_for("index")) # 存入session暂存 session['current_p_id'] = p_id return render_template("add_main_img.html") else: # 从session取出p_id p_id = session.get('current_p_id') if not p_id: flash("会话超时或参数丢失") return redirect(url_for("index")) # 后续的文件上传逻辑不变 if 'file' not in request.files: flash('No file part') return render_template("add_main_img.html") file = request.files['file'] if file.filename == '': flash("No image selected for upload") return redirect(request.url) if file and allowed_file(file.filename): filename = secure_filename(file.filename) file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) flash("Image succesfully uploaded") # 用完可以删掉session里的p_id,避免后续干扰 session.pop('current_p_id', None) return redirect(url_for('view', p_id=p_id)) else: flash("Allowed image types are .png, .jpg, .jpeg") return redirect(request.url)
额外优化建议
- 修正
view路由的数据库查询逻辑,避免IndexError:如果数据库里没有对应p_id的记录,fetchall()会返回空列表,直接取[0]会报错,所以要先判断:
@app.route("/view/<p_id>", methods=["GET", "POST"]) def view(p_id): """View an individual P""" rows = cursor.execute("SELECT * FROM pez WHERE p_id = ?", (p_id, )).fetchall() if not rows: flash("该记录不存在") return redirect(url_for("index")) rows = rows[0] series = rows[1] subseries = rows[2] name = rows[3] issue = rows[4] if request.method == "GET": return render_template("view.html", series = series, subseries = subseries, name = name, issue = issue, p_id = p_id)
- 确保你已经定义了
allowed_file函数(你代码里调用了但没贴出来):
def allowed_file(filename): return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
- 关于SQLite连接:
check_same_thread=False虽然能临时解决线程问题,但更规范的做法是在每个请求里获取连接,比如用Flask的before_request和teardown_request来管理连接,避免潜在的线程安全问题。
总结
你的核心问题就是POST请求时没传递p_id,和会话无关。上面两个方案都能解决,推荐方案1,更直接直观;如果涉及多步骤操作,方案2用Session会更方便。
备注:内容来源于stack exchange,提问作者DeyanD

