如何避免 UniqueViolation 错误

How to avoid UniqueViolation errors

我的类似 facebook 的应用程序让用户有机会删除来自关注用户的不需要的消息。为此,我在用户和微博之间创建了一个 MicropostQuarantine 关联:如果用户和微博之间存在这样的关联,那么该用户将不会在他们的提要中看到该微博。然而,微博也将在用户的个人资料页面中可见,包括那些已经被隔离的用户。因此,如果我从我的提要中删除了一个微博并访问了该微博用户的个人资料页面,我仍然会看到该微博并可以访问会引发以下错误的删除按钮:

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 (, , , ) 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 方法是 rails 出于某些未知原因,我一直从 ActiveRecord 和整个 ignored/bypassed 收到 UniqueViolation: ERROR =19=]条件被忽略,这样我就可以隔离非关注用户的微博。

我认为我们不应该在复杂的情况下使用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}

希望对您有所帮助!