Rails嵌套资源创建Question对象失败问题求助
看起来你在嵌套资源的使用上踩了几个常见的坑,我来帮你一步步排查和修复:
1. 路由助手调用错误
在create动作的成功分支里,你用了redirect_to coin_question(@question.coin_id, @question.id),这是不对的——Rails的路由助手方法名应该是coin_question_path(或者coin_question_url,前者用于相对路径),少了_path后缀会导致方法找不到,进而触发错误,最终走else分支渲染new页面。
2. 新建Question时未关联对应的Coin
你的new动作里,@question = current_user.questions.build只是关联了用户,但没有关联当前的@coin。这样一来,即使表单提交,coin_id可能没有被正确赋值(或者依赖用户提交,存在安全风险),导致@question.save失败。
正确的做法是通过@coin来构建关联的Question,这样会自动把coin_id赋值好,不需要在表单里处理:
def new @question = @coin.questions.build(user: current_user) end def create @question = @coin.questions.build(question_params.merge(user: current_user)) # ... 后续逻辑 end
3. 表单路由可以更简洁(且不易出错)
你手动指定了url: coin_questions_path(@coin.id),其实Rails支持直接用嵌套的对象数组来生成正确的路由,把simple_form_for改成这样:
<%= simple_form_for [@coin, @question] do |f| %> <%= f.input :ques_num %> <%= f.input :content %> <%= f.submit "Post", class: "btn btn-primary" %> <% end %>
这样Rails会自动识别这是嵌套资源的创建路由(POST /coins/:coin_id/questions),比手动写url更可靠。
4. 移除参数里的coin_id(安全优化)
既然我们通过关联构建Question,已经自动赋值了coin_id,就不需要在question_params里允许coin_id参数了——避免恶意用户篡改这个值,把question_params改成:
def question_params params.require(:question).permit(:content, :ques_num) end
修改后的完整代码参考
QuestionsController
class QuestionsController < ApplicationController before_action :find_coin before_action :find_question, only: [:show] before_action :authenticate_user!, except: [:index, :show] def index @questions = @coin.questions.order("created_at DESC") end def show end def new @question = @coin.questions.build(user: current_user) end def create @question = @coin.questions.build(question_params.merge(user: current_user)) if @question.save redirect_to coin_question_path(@coin, @question), notice: "Question created successfully!" else render 'new' end end private def find_question @question = Question.find(params[:id]) end def find_coin @coin = Coin.find(params[:coin_id]) end def question_params params.require(:question).permit(:content, :ques_num) end end
new页面表单
<%= simple_form_for [@coin, @question] do |f| %> <%# 如果有错误信息,可以在这里显示,方便调试 %> <% if @question.errors.any? %> <div class="alert alert-danger"> <ul> <% @question.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <%= f.input :ques_num %> <%= f.input :content %> <%= f.submit "Post", class: "btn btn-primary" %> <% end %>
另外,建议你保留表单里的错误信息显示,这样保存失败时能直观看到问题原因,方便后续调试。
内容的提问来源于stack exchange,提问作者mattC

