嵌套资源的Ajax Destroy方法是如何导致其父资源被销毁的



我有这样设置的资源:

resources :scoreboards do
 resources :teams
end

在我的scoreboard#show视图页面上,我有一个team模型的集合,它为每个团队生成一个div。每个团队div旁边都有一个Delete按钮,该按钮路由到teams_controller中的一个方法以删除该团队。

以下是与之相关的所有代码的列表:

团队旁边的删除按钮

<div>team example</div> <%= link_to "Del", scoreboard_team_path(@scoreboard, team), remote: true, method: :delete, class: "btn btn-primary" %>

按钮的Teams_controller方法

def destroy
     @scoreboard = Scoreboard.find(params[:scoreboard_id])
     @team = @scoreboard.teams.find(params[:id])
     @team.destroy
      respond_to do |format|
         format.html {redirect_to scoreboard_url(@scoreboard)}
         format.js
     end
   end

destroy.js.erb文件

$( "#team_<%=@team.id%>" ).hide();

现在的问题是,每当我快速连续地点击删除按钮(一个按钮上两次或多次)时,所有Ajax删除按钮都会停止工作。这很可能是因为团队关联的记分牌资源已被删除,因为我在Scoreboards_Controller:中收到以下错误

 NoMethodError in ScoreboardsController#show
undefined method `teams' for nil:NilClass
 def show
  @scoreboard = Scoreboard.find_by_id(params[:id])
  @team = @scoreboard.teams.build  # new team form on the page
  @comment = @scoreboard.comments.new
  @schedule = @scoreboard.schedules.build
 end

然后,当我查看记分牌列表时,与@团队关联的@记分牌资源已不存在。为什么会发生这种情况?

编辑:检查开发日志已经澄清了发生了什么。

所以我很快点击删除,它会破坏与该删除按钮相关的团队。由于在destroy.js.erb文件处理(隐藏已删除的div)之前,我成功地单击了两次删除按钮,Teams_Controller#destroy方法将在已删除的团队上再次运行,但没有任何内容可删除,因此Teams_Controller#destroy继续重定向到@scoreboard。现在,由于某种原因,Scoreboards_Controller#destroy执行并删除@scoreboard,之后它再次尝试重定向,并遇到路由错误,因为记分板已不存在。

以下是一些需要澄清的日志:

 Started DELETE "/scoreboards/45/teams/478" for 99.000.000.000 at 2015-12-12 03:54:09 +0000
Processing by TeamsController#destroy as JS
  Parameters: {"scoreboard_id"=>"45", "id"=>"478"}
  [1m[36mScoreboard Load (0.3ms)[0m  [1mSELECT  "scoreboards".* FROM "scoreboards" WHERE "scoreboards"."id" = ?  ORDER BY "scoreboards"."created_at" DESC LIMIT 1[0m  [["id", 45]]
  [1m[35mTeam Load (0.2ms)[0m  SELECT  "teams".* FROM "teams" WHERE "teams"."scoreboard_id" = ? AND "teams"."id" = ? LIMIT 1  [["scoreboard_id", 45], ["id", 478]]
  [1m[36m (0.3ms)[0m  [1mbegin transaction[0m
  [1m[35mSQL (0.4ms)[0m  DELETE FROM "teams" WHERE "teams"."id" = ?  [["id", 478]]
  [1m[36m (10.6ms)[0m  [1mcommit transaction[0m
  Rendered teams/destroy.js.erb (0.2ms)
Completed 200 OK in 48ms (Views: 28.1ms | ActiveRecord: 11.8ms)

   Started DELETE "/scoreboards/45/teams/478" for 99.000.000.000 at 2015-12-12 03:54:09 +0000
Processing by TeamsController#destroy as JS
  Parameters: {"scoreboard_id"=>"45", "id"=>"478"}
  [1m[35mScoreboard Load (0.3ms)[0m  SELECT  "scoreboards".* FROM "scoreboards" WHERE "scoreboards"."id" = ?  ORDER BY "scoreboards"."created_at" DESC LIMIT 1  [["id", 45]]
  [1m[36mTeam Load (0.2ms)[0m  [1mSELECT  "teams".* FROM "teams" WHERE "teams"."scoreboard_id" = ? AND "teams"."id" = ? LIMIT 1[0m  [["scoreboard_id", 45], ["id", 478]]
Redirected to https://score-app-kpauls.c9.io/scoreboards/45
Completed 302 Found in 19ms (ActiveRecord: 0.5ms)
Started DELETE "/scoreboards/45" for 99.000.000.000 at 2015-12-12 03:54:09 +0000
Processing by ScoreboardsController#destroy as JS
  Parameters: {"id"=>"45"}
  [1m[35mScoreboard Load (0.2ms)[0m  SELECT  "scoreboards".* FROM "scoreboards" WHERE "scoreboards"."id" = ?  ORDER BY "scoreboards"."created_at" DESC LIMIT 1  [["id", 45]]
  [1m[36mUser Load (0.1ms)[0m  [1mSELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1[0m  [["id", 105]]
  [1m[35mCACHE (0.0ms)[0m  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 105]]
  [1m[36mCACHE (0.0ms)[0m  [1mSELECT  "scoreboards".* FROM "scoreboards" WHERE "scoreboards"."id" = ?  ORDER BY "scoreboards"."created_at" DESC LIMIT 1[0m  [["id", "45"]]
  [1m[35m (0.2ms)[0m  begin transaction
  [1m[36mTeam Load (0.1ms)[0m  [1mSELECT "teams".* FROM "teams" WHERE "teams"."scoreboard_id" = ?[0m  [["scoreboard_id", 45]]
  [1m[35mSQL (0.3ms)[0m  DELETE FROM "teams" WHERE "teams"."id" = ?  [["id", 479]]
  [1m[36mSQL (0.0ms)[0m  [1mDELETE FROM "teams" WHERE "teams"."id" = ?[0m  [["id", 480]]
  [1m[35mSQL (0.1ms)[0m  DELETE FROM "teams" WHERE "teams"."id" = ?  [["id", 481]]
  [1m[36mSQL (0.1ms)[0m  [1mDELETE FROM "teams" WHERE "teams"."id" = ?[0m  [["id", 482]]
  [1m[35mComment Load (0.1ms)[0m  SELECT "comments".* FROM "comments" WHERE "comments"."scoreboard_id" = ?  [["scoreboard_id", 45]]
  [1m[36mSQL (0.1ms)[0m  [1mDELETE FROM "scoreboards" WHERE "scoreboards"."id" = ?[0m  [["id", 45]]
  [1m[35m (14.1ms)[0m  commit transaction
Redirected to https://score-app-kpauls.c9.io/scoreboards
Completed 302 Found in 39ms (ActiveRecord: 15.6ms)

在此之后,程序遇到路由错误。我会继续调查,但如果有人能帮助找到调用scorboards_controller#destroy的原因,我将不胜感激。

问题更新:

所以我已经解决了问题。我在application_controller文件中有这两种方法。

 rescue_from ActiveRecord::RecordNotFound do
  flash[:warning] = 'Resource not found.'
  redirect_back_or root_path 
 end
  def redirect_back_or(path)
   redirect_to request.referer || path
  end

每当我连续两次快速点击删除按钮时,销毁操作就会被重新路由到记分牌#show页面,并在第二次点击时继续调用该资源的销毁方法。这是因为调用destroy方法的@team在第一次单击时已经被破坏,因此请求重定向。在我刷新页面进入主页后,我确实收到了这些快闪消息,但一开始我认为它们是相关的,但它们是得出结论的关键。

代码基础结构看起来不错,我建议查看关联并确保team belongs_to :scoreboard 上没有dependent: :destroy

--

关于多个"删除"按钮单击的问题,问题看起来像是重定向到父资源。我没有任何理由会出现这种情况,只是Rails可能有一套内置的功能,可以在子路由失败时加载"父"路由。

我解决这个问题的方法是使用条件:

def destroy
     @scoreboard = Scoreboard.find(params[:scoreboard_id])
     @team = @scoreboard.teams.find params[:id]
     if @team.destroy
       respond_to do |format|
         format.html {redirect_to scoreboard_url(@scoreboard)} #-> could this be the reason for the redirect?????
         format.js
       end
     else
         redirect_to scoreboard_teams_path(@scoreboard), notice: "Team Already Deleted"
     end
end

我还想看看@team-if @team && @team.destroy-如果你想要更多信息,我可以重构。

这样做将为您提供一个可以处理异常的明确流程。我认为问题在于,当您单击删除按钮(记录不再存在)时,Rails无法处理异常。

Rails返回错误的内置方式是重定向到object_path(@object),并显示错误(就像您在format.html中看到的那样)。

因此,我猜Rails正试图将您带回@scoreboardscoreboard_path(@scoreboard)),并且由于您有method: :delete,它正在为该控制器运行destroy方法。

要修复它,您需要使用上面的条件让Rails知道在出现问题时该怎么做。

在你的teams_controller销毁操作中,我建议你更改这一行:

@team = @scoreboard.teams.find(params[:id])

对于

@team = Team.find(params[:id]) 

最新更新