我有两个要插入的Rails模型/表作为事务的一部分。
例如,以下是我的表格:
现在我正尝试等效于:
ActiveRecord::Base.transaction do
Comment.create!(post: post, user: user, comment_text: '...')
begin
Commenters.find_or_create_by!(post: post, user: user)
rescue PG::UniqueViolation
end
end
这工作99.9%的时间,但有时两个并发注释将触发PG :: UniqueViolation。
即使我捕获并抑制了PG :: UniqueViolation,由于以下原因,整个事务也会失败:
错误:当前事务中止,命令被忽略,直到事务块结束
我意识到我可以通过加入Post and Comment表来实现这一点,但这是一个简化的示例。
是否有一种更简单的方法来确保两次插入都作为事务的一部分发生,同时又可以忽略该唯一违规,因为我们可以假设记录已经存在?
答案 0 :(得分:0)
您应该在模型内部正确设置关联,因此Rails会为您进行验证。然后,您可以简单地挽救可能的ActiveRecord::RecordInvalid
(带有消息Validation failed:
Attribute has already been taken
)。
如果您想了解有关唯一性验证的更多信息,这应该派上用场:http://guides.rubyonrails.org/active_record_validations.html#uniqueness
在我看来,实际上您的Commenter
模型实际上并不是必需的。它仅包含派生信息,这些信息也可以直接从Comments
模型中提取(您已经存储了post_id
和user_id
),因此可以完全删除Commenter
类。然后注意通过设置例如{p>
Comment
但是,这样一来,您将只允许用户评论一次。
我建议您放弃唯一性约束,并通过在class Comment
belongs_to :post
belongs_to :user
validates :user_id, uniqueness: {scope: :post_id}
end
模型上进行Commenter
选择来构造存储在distinct
中的信息。
PS:rails中的模型以大写和单数形式编写,而表(以符号形式)以小写和复数形式引用。
答案 1 :(得分:0)
在事务内部引发的异常有两件事:
因此,您可以将异常处理程序移至transaction
调用之外:
begin
ActiveRecord::Base.transaction do
Comment.create!(post: post, user: user, comment_text: '...')
Commenters.find_or_create_by!(post: post, user: user)
end
rescue PG::UniqueViolation
retry
end
如果您想提高安全性,可以添加一个计数器仅重试几次。