在控制器中保存记录后如何触发动作

How to trigger an action after saving record in controller

我有一个简单的应用程序,用户可以在其中创建 projectstimetrackers,其中 belong_toprojects 基本上是时间戳。 timetrackers 有一个 start_time 和一个 end_time(均为 :datetime),现在我想计算两个值之间的持续时间并将其保存到 timespan 中,即 :float。

为此,我在 timetrackers_controller

中执行了 set_timespan 操作
  def set_timespan
    @job = Job.find(params[:job_id])
    @timetracker = Timetracker.find(params[:id])

    @timetracker.timespan = (@timetracker.end_time - @timetracker.start_time).round / 3600
  end

创建操作

  def create
    @job = Job.find(params[:job_id])
    @timetrackers = @job.timetrackers.new(timetracker_params)
    @timetrackers.user_id = current_user.id
    @timetrackers.job_id = @job.id
    #set_timespan


    respond_to do |format|
      if @timetrackers.save
        format.html { redirect_to @job, notice: 'Timestamps created successfully.' }
        format.json { render :show, status: :created, location: @job }
      else
        format.html { redirect_to new_timetracker_path, notice: "Please fill out the form." }
        format.json { render json: @job.errors, status: :unprocessable_entity }
      end
    end
  end

当我尝试在 @timetrackers.save 之前的 create 动作中拍摄动作时,出现 Couldn't find Timetracker without an ID 错误。我也被重定向到 jobs/3/timetrackers。为什么找不到 ID,我如何才能在我的控制器中正确触发一个动作来计算时间跨度?稍后我想总结一下时间跨度。

您仍然可以访问 @timetrackers 实例变量,作为一个简单的解决方案:

def set_timespan
  @job = Job.find(params[:job_id])

  @timetrackers.timespan = (@timetracker.end_time - @timetracker.start_time).round / 3600
end

我会更进一步,完全删除该方法,因为 @job 也可用,只需在创建操作中直接使用相关行:

def create
  # ...
  @timetrackers.job_id = @job.id
  # v here v
  @timetrackers.timespan = (@timetrackers.end_time - @timetrackers.start_time).round / 3600

  respond_to do |format|
  # ...
end

作为最终的,也许是理想的解决方案,您可以在 Timespan 模型中使用 before_create 回调:

# timespan.rb
before_create -> { self.timespan = (end_time - start_time).round / 3600 }

这样,您的 timespan 将在创建时自动设置。

我知道你已经有了喜欢的答案,但是...

另一种方法是修饰您的 timetracker_params,以便它们使用 new_timetracker_attributes 之类的方法包含所有适当的数据。可能是这样的:

  def create
    @job = Job.find(params[:job_id])
    @timetrackers = @job.timetrackers.new(new_timetracker_attributes)

    respond_to do |format|
      if @timetrackers.save
        format.html { redirect_to @job, notice: 'Timestamps created successfully.' }
        format.json { render :show, status: :created, location: @job }
      else
        format.html { redirect_to new_timetracker_path, notice: "Please fill out the form." }
        format.json { render json: @job.errors, status: :unprocessable_entity }
      end
    end
  end

private 

  def new_timetracker_attributes
    timetracker_params.merge(
      user_id:  current_user.id,
      timespan: ((end_time-start_time).round/3600)
    )
  end

  def timetracker_params
    params.require(:timetracker).permit(:start_time, :end_time)
  end

  def start_time
    timetracker_params[:start_time]
  end

  def end_time
    timetracker_params[:end_time]
  end

修饰 timetracker_params 的方法有很多种,这只是其中一种方法。

重点是,您的新实例属性的 所有 设置在 一个地方 而不是散布在不同的地方在你的应用程序中。 IMO,它让我们更容易理解正在发生的事情,并在事情出现问题时进行调试。