#ruby-on-rails #ruby-on-rails-4
#ruby-on-rails #ruby-on-rails-4
Вопрос:
У меня есть что-то вроде этого:
types = ['landline', 'cell']
Phone.find_or_create_by(person_id: 1, type: types) do |record|
record.number = '0'
end
Это не работает. Новые записи не создаются. Но когда я переписываю его, чтобы он выглядел так:
types = ['landline', 'cell']
types.each do |type|
Phone.find_or_create_by(person_id: 1, type: type) do |record|
record.number = '0'
end
end
это работает.
Есть идеи, почему find_or_create_by
не работает с массивом в качестве условия?
Комментарии:
1. Вы хотите создать 1 новый объект класса Phone или 2 новых объекта?
find_or_create_by
предназначен специально для 1 объекта и только для 1 объекта.2. Я хотел бы создать 2 новых объекта. Есть ли что-то подобное
find_or_create_by
, что создало бы 2 новых объекта?3. Нет. Вы либо используете итератор во втором блоке кода, как показано, либо перемещаете эту логику в пользовательский метод, если собираетесь вызывать его в нескольких разных местах.
4. Можете ли вы описать проблему в простой истории пользователя? Например, «Пользователь должен иметь возможность создать свою учетную запись с несколькими номерами телефонов …».
Ответ №1:
Хороший вопрос, вот мое объяснение с примером.
find_or_create_by
сначала выполняется select query
, а затем выполняется для create method
В документации api для find_or_create_by есть примечание,
Пожалуйста, обратите внимание, что этот метод не является атомарным, он сначала выполняет SELECT , и если результатов нет, выполняется попытка INSERT . Если есть другие потоки или процессы, между обоими вызовами существует условие гонки, и может случиться так, что в итоге вы получите две похожие записи.
Вот ссылка на find_or_create_by
итак, когда выполняется эта команда, сначала выполняется запрос select .
Например, позвольте мне взять таблицу пользователя и показать вам эту таблицу из результата консоли.
У меня есть пользователь с электронной почтой test222@example.com
в базе данных, но нет, test333@example.com
.
User.find_or_create_by(email: ['test222@example.com','test333@example.com'])
теперь, когда я запускаю find_or_create_by
this, генерируется запрос.
User Load (175.5ms) SELECT
пользователи .* FROM
пользователи WHERE
пользователи .
электронная почта IN ('test222@example.com', 'test333@example.com') LIMIT 1
Ответ,
=> #<User id: 82, provider: "email", uid: "test222@example.com", name: nil, nickname: nil, image: nil, email: "test222@example.com", created_at: "2016-09-05 12:35:01", updated_at: "2016-09-05 12:35:01">
Таким образом, он вернул найденного пользователя и не запустил метод create, игнорируя not found(второе электронное письмо)
Теперь, если я запускаю его в цикле,
emails = ['test222@example.com','test333@example.com']
emails.each do |email|
User.find_or_create_by(email: email)
end
Запрос INSERT будет выполнен для второго электронного письма,
**INSERT INTO `users` (`email`,`created_at`, `updated_at`) VALUES ('test@ead.com', '2016-10-07 13:16:25', '2016-10-07 13:16:25')**
Это тот же случай и в вашем случае.