Rails 5: Найти или создать с .each do - PullRequest
0 голосов
/ 30 марта 2019

Я делаю GET запрос на Typeform.Если запрос выполнен успешно, я сохраняю ответ как 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 голосов
/ 31 марта 2019

Документация активной записи для find_or_create_by :

Обратите внимание, этот метод не атомарный , он сначала запускает SELECT, и, если нет результатов, предпринимается попытка INSERT. Если существуют другие потоки или процессы, между обоими вызовами возникает условие состязания, и может случиться так, что вы получите две одинаковые записи.

В любом случае вы можете обойти эту проблему, добавив ограничение uniq к комбинации столбцов user_id и landing_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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...