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

时间:2015-12-11 23:48:11

标签: ruby-on-rails ajax

我的资源设置如下:

resources :scoreboards do
 resources :teams
end

在我的scoreboard#show视图页面上,我有一个team模型的集合,为每个团队生成一个div。除了每个team-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

然后当我查看记分板列表时,@ team所关联的@Scoreboard资源不再存在。为什么会这样?

编辑:检查开发日志已经阐明了正在发生的事情。

所以我非常快地点击删除它会破坏与该删除按钮相关联的团队。由于我设法在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)

此后程序遇到路由错误。我将继续研究它,但如果有人能帮助找到为什么要调用scoreboards_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,因此请求重定向。我刷新页面进入主页后确实收到了flash消息,但最初认为它们是相关的,但它们是结论的关键。

2 个答案:

答案 0 :(得分:1)

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

-

关于多个“删除”按钮单击的问题,问题看起来就像您正在重定向到父资源。我没有任何理由为什么会这样,除非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知道在遇到问题时该怎么做。

答案 1 :(得分:1)

在destroy action中的teams_controller中,我建议你更改这一行:

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

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