Rails 5:使用.each进行查找或创建

时间:2019-03-30 19:31:27

标签: ruby-on-rails ruby-on-rails-5

我正在向Typeform发出GET请求。如果请求成功,则将响应另存为order到数据库。我的问题在于这一特定部分:

current_user.find_or_create_by(landing_id: item["landing_id]) do |order|

如果用户提交新的Typeform,则该方法将找不到landing_id,因此将再次创建所有orders。但是这些orders已经存在于数据库中,因此每个order被多次保存到我的数据库中。

如何避免这种情况?

items = response.parsed_response["items"]
 items.each do |item|
  @order = current_user.orders.find_or_create_by(landing_id: item["landing_id"]) do |order|
   order.landing_id = item["landing_id"]
   order.email = item["hidden"]["email"]
   order.price = item["hidden"]["price"]
   order.project = item["hidden"]["project"]
   order.save!
  end
 end

1 个答案:

答案 0 :(得分:0)

find_or_create_by的Active Record文档:

  

请注意此方法不是原子的,它首先运行SELECT,如果没有结果,则尝试INSERT。如果还有其他线程或进程,则这两个调用之间存在竞争状态,并且最终可能会有两个相似的记录。

无论如何,您可以通过为user_idlanding_id列的组合添加uniq约束来解决此问题:

add_index :orders, [:user_id, :landing_id], unique: true

并相应地更改代码:

items = response.parsed_response["items"]
items.each do |item|
  begin
    Order.transaction(requires_new: true) do
      @order = current_user.orders.find_or_create_by(landing_id: item["landing_id"]) do |order|
        order.landing_id = item["landing_id"]
        order.email = item["hidden"]["email"]
        order.price = item["hidden"]["price"]
        order.project = item["hidden"]["project"]
        order.save!
      end
    end
  rescue ActiveRecord::RecordNotUnique
    next
  end
end