如何避免UniqueViolation错误

时间:2018-01-22 13:08:46

标签: ruby-on-rails activerecord associations

我的类Facebook应用程序让用户有机会从以下用户中删除不需要的消息。为此,我在用户和微博之间创建了MicropostQuarantine关联:如果存在用户和微博之间的这种关联,那么该用户将不会在其提要中看到该微博。但是,微博也会在用户的个人资料页面中显示,包括那些已经隔离的微博。因此,如果我从我的Feed中删除了微博并访问了该微博用户的个人资料页面,我仍然会看到该微博并且可以访问会导致以下错误的删除按钮:

ActiveRecord::RecordNotUnique (PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_micropost_quarantines_on_user_id_and_micropost_id"
DETAIL:  Key (user_id, micropost_id)=(1, 300) already exists.
: INSERT INTO "micropost_quarantines" ("user_id", "micropost_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"):

微博控制器中的隔离方法很简单:

def quarantine
  current_user.micropost_quarantines.create!(micropost_id: @micropost.id)
end

before_action :entitled_user, only: :quarantine定义如下:

def entitled_user
  @micropost = Micropost.find(params[:id])
  redirect_to root_url unless (current_user == @micropost.user || current_user.following.include?(@micropost.user))
end

正如您所看到的,用户只能隔离来自后续用户的微博和微博。为了避免UniqueViolation错误,我想到为entitled_user方法添加一些代码,以检查关联是否已经存在:

def entitled_user
  @micropost = Micropost.find(params[:id])
  @quarantine = MicropostQuarantine.find_by(micropost_id: @micropost.id, user_id: current_user.id)
  redirect_to root_url unless (current_user == @micropost.user || current_user.following.include?(@micropost.user) || @quarantine.nil?)
end    

但是这不起作用:导出忽略/绕过entitled_user方法由于某些未知原因,我一直从ActiveRecord接收UniqueViolation: ERROR并忽略整个unless条件,这样我就可以隔离非后续用户的微博。

1 个答案:

答案 0 :(得分:1)

我认为我们不应该在复杂条件下使用unless,请尝试以下方法:

redirect_to root_url if (current_user != @micropost.user && current_user.following.exclude?(@micropost.user)) || @quarantine.present?

提示:

def entitled_user
  @micropost = Micropost.find(params[:id])

  if (current_user.id != @micropost.user_id && !current_user.following.exists?(@micropost.user_id)) ||
     current_user.micropost_quarantines.exists?(micropost_id: @micropost.id)

    redirect_to root_path and return
  end
end
  • 使用:exists?(SQL端)比:include?(Ruby端)更有效
  • 使用@micropost.user_id代替@micropost.user因为我们不需要实例@user,所以我们不需要这样做:

SELECT (*) FROM users WHERE id = #{@micropost.user_id}

希望这有帮助!