Flask/Jinja实现单张狗狗图片评分后切换的方法求助
实现Hot or Not式单张图片切换方案
给你两个简单可行的实现方案,优先选第一个,改动最小、见效最快:
方案一:纯前端JavaScript控制(无需大量修改后端)
这个方案直接把所有图片加载到页面,用JS控制只显示当前张,评分后切换到下一张,适合快速上线。
1. 优化后端数据处理(避免前端冗余replace)
先修改app.py的arena函数,把数据库返回的元组列表转换成纯图片路径的列表,这样前端不用一堆replace:
@app.route("/arena", methods=["GET", "POST"]) @login_required def arena(): if request.method == "GET": try: conn = sqlite3.connect('dogs.db') cursor = conn.cursor() sql = "SELECT photo_address FROM photos;" cursor.execute(sql) rows = cursor.fetchall() # 把元组列表转成纯字符串列表 photos = [row[0] for row in rows] except sqlite3.Error as er: conn.rollback() print('SQLite error: %s' % (' '.join(er.args))) flash("Oops, There was an gathering error!") return render_template("arena.html") else: conn.close() return render_template("arena.html", photos=photos)
2. 修改前端页面(arena.html)
添加CSS控制显示隐藏,加评分按钮,用JS实现切换逻辑:
{% extends "layout.html" %} {% block title %} Arena {% endblock %} {% block head %} <style> /* 默认隐藏所有图片,只显示激活的那张 */ .dog-photo { display: none; margin: 0 auto; max-width: 80%; max-height: 60vh; } .dog-photo.active { display: block; } .rating-buttons { margin-top: 20px; text-align: center; } .rating-buttons button { padding: 10px 24px; font-size: 18px; margin: 0 12px; cursor: pointer; border: none; border-radius: 8px; } #btn-like { background-color: #4CAF50; color: white; } #btn-dislike { background-color: #f44336; color: white; } </style> {% endblock %} {% block main %} <div class="photo-wrapper"> {% for photo in photos %} <div class="dog-photo {% if loop.first %}active{% endif %}"> <img src="{{ photo }}" alt="狗狗照片"> </div> {% endfor %} </div> <div class="rating-buttons"> <button id="btn-dislike">👎 不喜欢</button> <button id="btn-like">👍 喜欢</button> </div> {% endblock %} {% block scripts %} <script> document.addEventListener('DOMContentLoaded', function() { const allPhotos = document.querySelectorAll('.dog-photo'); let currentIndex = 0; // 显示当前索引对应的图片,隐藏其他 function showCurrent() { allPhotos.forEach((photo, index) => { photo.classList.toggle('active', index === currentIndex); }); } // 切换到下一张 function nextPhoto() { currentIndex++; // 到最后一张后重置到开头,也可以改成提示结束 if (currentIndex >= allPhotos.length) { alert('所有狗狗都看完啦!'); currentIndex = 0; } showCurrent(); } // 绑定按钮点击事件 document.getElementById('btn-like').addEventListener('click', nextPhoto); document.getElementById('btn-dislike').addEventListener('click', nextPhoto); }); </script> {% endblock %}
方案二:后端API配合(支持记录评分)
如果需要把用户的评分保存到数据库,避免重复评分,就用这个方案,通过AJAX请求获取下一张未评分的图片。
1. 后端修改
首先确保数据库有ratings表(可自行创建:CREATE TABLE ratings (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, photo_id INTEGER, rating TEXT);),然后添加两个接口:
# 获取下一张未评分的图片 @app.route("/get_next_photo", methods=["GET"]) @login_required def get_next_photo(): try: conn = sqlite3.connect('dogs.db') cursor = conn.cursor() # 先查用户没评过分的图片 cursor.execute(""" SELECT id, photo_address FROM photos WHERE id NOT IN (SELECT photo_id FROM ratings WHERE user_id = ?) LIMIT 1 """, (session['user_id'],)) photo = cursor.fetchone() except sqlite3.Error as er: print('SQLite error:', er) photo = None finally: conn.close() if photo: return jsonify({"photo_id": photo[0], "photo_url": photo[1]}) else: return jsonify({"photo_url": None}) # 提交评分 @app.route("/submit_rating", methods=["POST"]) @login_required def submit_rating(): photo_id = request.form.get("photo_id") rating = request.form.get("rating") if not photo_id or not rating: return jsonify({"success": False}) try: conn = sqlite3.connect('dogs.db') cursor = conn.cursor() cursor.execute(""" INSERT INTO ratings (user_id, photo_id, rating) VALUES (?, ?, ?) """, (session['user_id'], photo_id, rating)) conn.commit() except sqlite3.Error as er: conn.rollback() print('SQLite error:', er) return jsonify({"success": False}) finally: conn.close() # 提交后直接返回下一张图片 return get_next_photo()
同时修改原来的arena路由,只渲染空页面,图片通过JS加载:
@app.route("/arena", methods=["GET", "POST"]) @login_required def arena(): return render_template("arena.html")
2. 前端修改(arena.html)
{% extends "layout.html" %} {% block title %} Arena {% endblock %} {% block head %} <style> #dog-photo { display: block; margin: 0 auto; max-width: 80%; max-height: 60vh; } .rating-buttons { margin-top: 20px; text-align: center; } .rating-buttons button { padding: 10px 24px; font-size: 18px; margin: 0 12px; cursor: pointer; border: none; border-radius: 8px; } #btn-like { background-color: #4CAF50; color: white; } #btn-dislike { background-color: #f44336; color: white; } </style> {% endblock %} {% block main %} <div id="photo-container"> <img id="dog-photo" alt="加载中..." src=""> </div> <div class="rating-buttons"> <button id="btn-dislike">👎 不喜欢</button> <button id="btn-like">👍 喜欢</button> </div> {% endblock %} {% block scripts %} <script> let currentPhotoId; // 加载下一张图片 function loadNextPhoto() { fetch("/get_next_photo") .then(res => res.json()) .then(data => { if (data.photo_url) { currentPhotoId = data.photo_id; document.getElementById("dog-photo").src = data.photo_url; } else { alert("所有狗狗都看完啦!"); document.getElementById("photo-container").style.display = "none"; } }); } // 提交评分 function sendRating(rating) { fetch("/submit_rating", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: `photo_id=${currentPhotoId}&rating=${rating}` }) .then(res => res.json()) .then(data => { if (data.success !== false) { if (data.photo_url) { currentPhotoId = data.photo_id; document.getElementById("dog-photo").src = data.photo_url; } else { alert("所有狗狗都看完啦!"); document.getElementById("photo-container").style.display = "none"; } } else { alert("评分提交失败,请重试"); } }); } // 页面加载时初始化 document.addEventListener("DOMContentLoaded", function() { loadNextPhoto(); document.getElementById("btn-like").addEventListener("click", () => sendRating("like")); document.getElementById("btn-dislike").addEventListener("click", () => sendRating("dislike")); }); </script> {% endblock %}
内容的提问来源于stack exchange,提问作者jnel90
相关产品推荐
相关产品推荐

