在控制器中使用回调时可以合并数据库命中?

时间:2015-03-28 00:50:37

标签: ruby-on-rails ruby

我发现自己试图尽可能地将代码分段为控制器逻辑的回调。但我开始担心性能问题。在下面的示例中,我必须在点击投票#update时查询相同的投票实例3x。反正有没有传递一个对象,以便我可以维护我的回调但只查询一次db?我在Rails中错过了一些机制吗?

class VoteController < ApplicationController
    #Access limitations etc.
    before_action :authenticate_user!
    before_action :correct_user_vote, only: [:edit, :update]
    before_action :election_concluded_check, only: [:update]

    def edit
        @vote = Vote.find(params[:id])
    end

    def update
        @vote = Vote.find(params[:id])

        if !@vote.nil? && @vote.update_attributes(vote_params)
            redirect_to party_path(@vote.party_id)
        else
            flash[:error] = "Something went wrong"
            redirect_to root_path
        end
    end

    private

        def correct_user_vote
            vote = Vote.find(params[:id])
            if vote.nil? || current_user.id!=vote.user_id
                flash[:error] = "Something went wrong"
                redirect_to root_path
            end
        end

        def election_concluded_check
            # Do not allow updating of votes if party.concluded
            vote = Vote.find(params[:id])
            if vote.nil? || vote.election.concluded
                flash[:error] = "Something went wrong"
                redirect_to root_path
            end
        end

        def vote_params
            params[:vote].permit(:candidate_id,:note)
        end
end

2 个答案:

答案 0 :(得分:2)

将您的投票加载到before_action中的实例变量中,然后在随后的所有before_action中使用它:

class VoteController < ApplicationController
  before_action :authenticate_user!
  before_action :find_vote, only: [:edit, :update]
  before_action :correct_user_vote, only: [:edit, :update]
  before_action :election_concluded_check, only: [:update]

  def edit
  end

  def update
    if @vote.update_attributes(vote_params)
      redirect_to party_path(@vote.party_id)
    else
      flash[:error] = "Something went wrong"
      redirect_to root_path
    end
  end

  private

  def find_vote
    @vote = Vote.find(params[:id])
  end

  def correct_user_vote
    if current_user.id != @vote.user_id
      flash[:error] = "Something went wrong"
      redirect_to root_path
    end
  end

  def election_concluded_check
    # Do not allow updating of votes if party.concluded
    if @vote.election.concluded
      flash[:error] = "Something went wrong"
      redirect_to root_path
    end
  end
end

您可以这样做的原因是实例变量(以@开头的变量)可以在同一对象的方法之间访问。因此,您可以在一个方法(@vote)中设置find_vote,然后在同一对象的其他方法中访问它(例如update)。在这种情况下,@vote仅在find_vote方法中加载一次。

另请注意,在@vote中加载before_action实例变量后,您不再需要在edit方法中执行任何操作,这是此方法的一个很好的副作用

作为旁注,假设VoteActiveRecord::Base子类,当您执行@vote = Vote.find(id)时,@vote对象永远不会是nil,因为如果ActiveRecord::Base#find找不到您要求的记录,则会引发异常。因此,我们确实无需检查@vote.nil?

答案 1 :(得分:-1)

通常的技术是使用在第一次访问时缓存对象的方法替换变量。例如:

  def edit
    @vote = vote
  end

private

  def vote
    @_vote ||= Vote.find(params[:id])
  end

  def correct_user_vote
    if vote.nil? || current_user.id != vote.user_id
      flash[:error] = "Something went wrong"
      redirect_to root_path
    end
  end

不幸的是,Rails认为使用实例变量将信息传递给您的视图是个好主意,因此您需要注意如何命名缓存值(因此@_vote而不是@vote