Rails国际象棋应用:Pieces控制器Show动作无法访问white_player_id
看起来你遇到的问题主要来自两个核心点:数据库字段未持久化和控制器代码的语法/逻辑错误,咱们一步步拆解解决:
1. Game模型回调未将white_player_id写入数据库
你的Game模型里的current_user_is_white_player是after_create回调,它只给white_player_id赋了值,但没有把这个变更保存到数据库里。模型测试能通过是因为测试中操作的是内存里的对象,而控制器读取的是数据库中的真实记录——数据库里的white_player_id其实是NULL,这就导致@piece.game.white_player_id返回nil,触发了render_not_found。
修复方案:
把回调改成before_create(在记录写入数据库前赋值,无需额外保存,更高效):
class Game < ApplicationRecord # 替换原来的after_create为before_create before_create :current_user_is_white_player def current_user_is_white_player self.white_player_id = user_id end # ... 其他代码保持不变 end
如果一定要用after_create,就在方法末尾加上save!强制保存变更,但before_create是更优的实现方式。
2. Pieces控制器show方法的语法与逻辑错误
你控制器里的代码存在注释导致的分支结构混乱:
if @piece.game.white_player_id # 控制器无法识别white_player_id,返回404错误 # if @piece.piece_color_matches_user_color?(current_user) return render plain: "Success" else render_not_found end
注释掉内层if后,else会和外层的if @piece.game.white_player_id配对——只要white_player_id存在就返回Success,这完全偏离了你要校验"棋子颜色匹配用户"的核心逻辑,而且如果white_player_id是nil(数据库里没值),就会进入else返回404。
修复方案:
恢复正确的逻辑判断,直接调用你已经在模型里测试通过的piece_color_matches_user_color?方法:
def show @piece = Piece.find_by_id(params[:id]) if @piece.blank? return render_not_found end if @piece.piece_color_matches_user_color?(current_user) render plain: "Success" else render_not_found end end
3. 额外检查:FactoryBot游戏工厂
确保你的game工厂会关联一个有效的用户,这样white_player_id才能被正确赋值:
# spec/factories/games.rb FactoryBot.define do factory :game do user { create(:user) } # 确保创建游戏时关联一个用户 state { "pending" } # 其他必要字段按需添加 end end
验证修复
完成以上修改后:
- 游戏创建时
white_player_id会被正确写入数据库 - 控制器会正确校验当前用户是否有权限查看该棋子
- 你的控制器测试应该能顺利通过,返回200 Success状态码
内容的提问来源于stack exchange,提问作者Elizabeth

