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

问题描述 投票:0回答:1

我正在向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
ruby-on-rails ruby-on-rails-5
1个回答
1
投票

find_or_create_by的Active Record文档:

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

无论如何,你可以通过为user_idlanding_id列的组合添加一个唯一约束来解决这个问题:

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
© www.soinside.com 2019 - 2024. All rights reserved.