Rails [求助] Rails 5.2 多重多态怎么获取正确的 url?

Sylor-huang · 2018年08月22日 · 最后由 Sylor-huang 回复于 2018年08月25日 · 1706 次阅读

如题。

我的部分代码为:


## question.rb
has_many :comments, :as => :commentable, :dependent => :destroy

## comment.rb
belongs_to :commentable, polymorphic: true
has_many :reports, :as => :reportable, :dependent => :destroy

## report.rb

belongs_to :reportable, polymorphic: true


## 路由 routes.rb

resources :questions do

  resources :comments do 

    resources :reports

end


## _comments.html.erb
<%= link_to "<i class='fa fa-times mr2 fa-fw'></i>Report".html_safe,new_question_comment_report_path(@question,@comment),remote: true %>
## reports_controller.rb

class ReportsController < ApplicationController
  before_action :logged_in_user
  before_action :set_reportable

  def new
    @report = @reportable.reports.new
    respond_to do |format|
      format.html do
        redirect_to @reportable
      end
      format.js
    end
  end

  def create
    @report = @reportable.reports.new report_params
    @report.reporter_id = current_user.id
    if @report.save
      redirect_to @reportable
    end
  end

  def destroy
    @report = Report.find(params[:id])
    @report.destroy
  end

  private
  def set_reportable
    if params[:comment_id]
      @question = Question.find(params[:question_id])
      @reportable = @question.comments.find(params[:comment_id])
    end
  end

  def report_params
    params.require(:report).permit(:content,:radio_content)
  end
end

## reports  new.js.erb为

$("body").append("<%= j render "reports/reports" %>")
$("#tip-offs").modal('show')


## _reports.html.erb
<div class="modal tip-offs in" id="tip-offs" >
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button class="close" data-dismiss="modal" type="button">
          <span aria-hidden="true">&times;</span> <span class="sr-only">Close</span>
        </button>
        <h4 class="modal-title">
          <span data-model="action">Report</span>
        </h4>
      </div>
      <%= form_for [@reportable,@report] do |f| %>
      <div class="modal-body">
          <div class="pbt5">
            <%= f.text_area :content,row:3, class: "form-control",placeholder: "report more" %>
          </div>
      </div>
      <div class="modal-footer">
        <button class="btn btn-default" data-dismiss="modal" type="button">Close</button>
        <%= f.submit "report",class:"btn btn-primary" %>
      </div>
        <% end %>
    </div>
    <!-- /.modal-content -->
  </div>
  <!-- /.modal-dialog -->
</div>

现在,如果在_comments.html.erb 中,new_question_comment_report_path(@question,@comment)的 url 为:/questions/6/comments/15/reports/new,

在我提交 report 时,提示错误为:

Processing by ReportsController#new as JS
  Parameters: {"question_id"=>"6", "comment_id"=>"15"}
  User Load (0.3ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
  Question Load (0.2ms)  SELECT  `questions`.* FROM `questions` WHERE `questions`.`id` = 10 LIMIT 1
  Comment Load (0.2ms)  SELECT  `comments`.* FROM `comments` WHERE `comments`.`commentable_id` = 6 AND `comments`.`commentable_type` = 'Question' AND `comments`.`id` = 15 LIMIT 1
  Rendering reports/new.js.erb
  Rendered reports/_reports.html.erb (24.4ms)
  Rendered reports/new.js.erb (25.8ms)
Completed 500 Internal Server Error in 38ms (ActiveRecord: 0.7ms)

NoMethodError - undefined method `comment_reports_path' for #<#<Class:0x00007fb020559858>:0x00007fb022d02c38>
Did you mean?  comment_like_tag:
  app/views/reports/_reports.html.erb:13:in `_app_views_reports__reports_html_erb___1071799134587169621_70197237478960'
  app/views/reports/new.js.erb:1:in `_app_views_reports_new_js_erb___4579025611719430071_70197237521140'

请问下,我是不是在 reports_controller 中的 set_reportable 有错误?如果是的话,我该怎么修改呢?非常感谢~

/rails/info/routes 看一下 comment_reports_path 有没有这个,需要加@comment参数的

AR 的对象有一个 become 方法~

日志贴全。

@Rei @jmmbite @jasl 感谢各位的热心解答,我更新了下问题,增加了_reports.html.erb和详细点的错误提示。

然后,我在 stackoverflow 上提问后,有人建议说修改_reports.html.erb中的

<%= form_for [@reportable,@report] do |f| %> 为:<%= form_for [@question,@reportable,@report] do |f| %>

这个是可以正确提交 reports 并获得@reportable的 type 和 id。

但是我想把_reports.html.erb作为通用的模板,继续使用<%= form_for [@reportable,@report] do |f| %>,并可以用在 questions、articles 等等模型中。那么我该怎么在reports_controller.rb中设置@reportable,可以获取正确的值?

如果有说的不清楚的地方,烦请指出。再次感谢热心帮助的大伙。

<%= form_for [@reportable,@report] do |f| %>

这里有错,根据这个参数 form 会推导两级的路由 comment_reports_path,但是路由里并没有这个路由,需要再加上 @question

<%= form_for [@question, @reportable, @report] do |f| %>

但是这里可以看到,本来想多态关联 reportable 让 report 跟灵活,却不得不插入不相关的 question,灵活性受阻。

这里不应该用多级路由,而是一级的 report 路由:

resources :reports

把 reportable_type 和 reportable_id 作为参数传。

@Rei 谢谢您的回答。我想请问下,reportable_type 和 reportable_id 作为参数传,这个可以举例说明下吗?

比如在_comments.html.erb中,现在是new_reports_path(@comment) 那么我该怎么在 reports_controller.rb 中,获得@comment的 type 和 id 呢?非常感谢。

class Report
  # 安全原因,阻止用户提交预料外的东西
  validates :reportable_type, inclusion: { in: %w(Comment) }
end
new_reports_path(reportable_type: 'Comment', reportable_id: @comment.id)
def new
  @report = Report.new report_params
end

def create
  @report = Reports.new report_params.merge(reporter: current_user)
  if @report.save
    # ...
  else
    # ...
  end
end

private

def report_params
  params.require(:report).permit(:reportable_type, :reportable_id, :content, :radio_content)
end
<%= form_for @report do |f| %>
  <%= f.hidden_field :reportable_type %>
  <%= f.hidden_field :reportable_id %>
<% end %>

@Rei 非常感谢大佬~~🌹

@Rei 请问下大哥:

class ReportsController < ApplicationController
  before_action :logged_in_user
  before_action :get_target

  def new
    @report = Report.new
    respond_to do |format|
      format.html do
        redirect_to @report_url
      end
      format.js
    end
  end

  def create
    @report = Report.new report_params.merge(reporter_id: current_user.id)

    if @report.save
      redirect_to @report_url
    else
      redirect_to home_path
    end
  end

  def destroy
    @report = Report.find(params[:id])
    @report.destroy
  end

  private

  def get_target
    @report_type = params["reportable_type"]
    @report_id = params["reportable_id"]
    report_able = Comment.find_by_id(@report_id.to_i)
    target_able_type = report_able.commentable_type.singularize.classify.constantize
    target_able_id = report_able.commentable_id.to_i
    @report_url = target_able_type.find_by_id(target_able_id)
  end

  def report_params
    params.require(:report).permit(:reportable_type, :reportable_id,:content,:radio_content)
  end
end

我按照您的代码,如我在/questions/15页面(即当前页面),提交 report 后,想再次跳转到当前页面,该怎么做呢?

在我的get_target action 中,如果按照 report_able = Comment.find_by_id(26) 那么@report_url 可以获取到正确的 url('/questions/15')。

但是如果是 report_able = Comment.find_by_id(@report_id.to_i),使用变量的话,则会提示错误:


127.0.0.1 - - [24/Aug/2018:00:36:11 CST] "GET /reports/new?reportable_id=26&reportable_type=Comment HTTP/1.1" 200 3584
http://127.0.0.1:3000/questions/15 -> /reports/new?reportable_id=26&reportable_type=Comment
Started POST "/reports" for 127.0.0.1 at 2018-08-24 00:36:13 +0800
Processing by ReportsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"u6iqI2AIxQXf63gmwjHYnJ+QzvDFX+8A0xPlwMtiY4QJcxkSul77gL1d7MnIRbAqU5Qxt4dsha7zZmNV8BNrww==", "report"=>{"reprtable_type"=>"Comment", "reportable_id"=>"26", "radio_content"=>"xxxx", "content"=>""}, "commit"=>"report"}
  User Load (0.4ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
  Comment Load (0.3ms)  SELECT  `comments`.* FROM `comments` WHERE (id = NULL) LIMIT 1
Completed 500 Internal Server Error in 5ms (ActiveRecord: 0.6ms)

NoMethodError - undefined method `commentable_id' for nil:NilClass:
  app/controllers/reports_controller.rb:44:in `get_target'

127.0.0.1 - - [24/Aug/2018:00:36:13 CST] "POST /reports HTTP/1.1" 500 75672
http://127.0.0.1:3000/questions/15 -> /reports
Started POST "/__better_errors/4b81417ea02a92e0/variables" for 127.0.0.1 at 2018-08-24 00:36:13 +0800
127.0.0.1 - - [24/Aug/2018:00:36:13 CST] "POST /__better_errors/4b81417ea02a92e0/variables HTTP/1.1" 200 88217
http://127.0.0.1:3000/reports -> /__better_errors/4b81417ea02a92e0/variables

但是@report_id.to_i输出也确实是 26。

请问下,是哪里出现错误了呢?非常感谢。@Rei

要回到前一页有几种方法:

  1. 逻辑判断 reportable 类型选择路由(回到顶楼问题)
  2. 根据 referer 跳转(有时会有奇怪问题)
  3. 既然是 modal,那么用 remote form,提交后关闭 modal 就行。

@Rei 非常感谢大佬的指点。

Sylor-huang 关闭了讨论。 08月25日 00:09
需要 登录 后方可回复, 如果你还没有账号请 注册新账号