Рельсы 6
Рельсы 6 добавляет upsert
и upsert_all
методы , которые обеспечивают эту функциональность.
Model.upsert(column_name: value)
[upsert] Он не создает экземпляров каких-либо моделей и не запускает обратные вызовы или проверки Active Record.
Рельсы 5, 4 и 3
Нет, если вы ищете оператор типа «upsert» (когда база данных выполняет обновление или оператор вставки в той же операции). По умолчанию Rails и ActiveRecord не имеют такой возможности. Однако вы можете использовать драгоценный камень upsert .
В противном случае вы можете использовать: find_or_initialize_by
или find_or_create_by
, которые предлагают аналогичную функциональность, хотя и за счет дополнительного обращения к базе данных, что в большинстве случаев вряд ли является проблемой. Поэтому, если у вас нет серьезных проблем с производительностью, я бы не стал использовать этот драгоценный камень.
Например, если не найдено ни одного пользователя с именем «Роджер», создается новый экземпляр пользователя с name
установленным значением «Роджер».
user = User.where(name: "Roger").first_or_initialize
user.email = "email@example.com"
user.save
В качестве альтернативы вы можете использовать find_or_initialize_by
.
user = User.find_or_initialize_by(name: "Roger")
В Rails 3.
user = User.find_or_initialize_by_name("Roger")
user.email = "email@example.com"
user.save
Вы можете использовать блок, но он запускается только в том случае, если запись новая .
User.where(name: "Roger").first_or_initialize do |user|
user.save
end
User.find_or_initialize_by(name: "Roger") do |user|
user.save
end
Если вы хотите использовать блок независимо от постоянства записи, используйте tap
результат:
User.where(name: "Roger").first_or_initialize.tap do |user|
user.email = "email@example.com"
user.save
end
find_or_initialize_by
иfind_or_create_by
принимают блок. Я думал, вы имели в виду, что независимо от того, существует запись или нет, для выполнения обновления будет передан блок с объектом записи в качестве аргумента.В Rails 4 вы можете добавить к конкретной модели:
def self.update_or_create(attributes) assign_or_new(attributes).save end def self.assign_or_new(attributes) obj = first || new obj.assign_attributes(attributes) obj end
и используйте это как
User.where(email: "a@b.com").update_or_create(name: "Mr A Bbb")
Или, если вы предпочитаете добавить эти методы ко всем моделям, вставьте инициализатор:
module ActiveRecordExtras module Relation extend ActiveSupport::Concern module ClassMethods def update_or_create(attributes) assign_or_new(attributes).save end def update_or_create!(attributes) assign_or_new(attributes).save! end def assign_or_new(attributes) obj = first || new obj.assign_attributes(attributes) obj end end end end ActiveRecord::Base.send :include, ActiveRecordExtras::Relation
источник
assign_or_new
вернет первую строку в таблице, если она существует, а затем эта строка будет обновлена? Похоже, это для меня.User.where(email: "a@b.com").first
вернет nil, если не найден. Убедитесь, что у вас естьwhere
прицелupdated_at
не будут затронуты, потому чтоassign_attributes
используетсяДобавьте это в свою модель:
def self.update_or_create_by(args, attributes) obj = self.find_or_create_by(args) obj.update(attributes) return obj end
Благодаря этому вы можете:
User.update_or_create_by({name: 'Joe'}, attributes)
источник
Магия вы искали в добавлен
Rails 6
Теперь вы можете upsert (обновление или вставка). Для использования одной записи:Model.upsert(column_name: value)
Для нескольких записей используйте upsert_all :
Model.upsert_all(column_name: value, unique_by: :column_name)
Примечание :
источник
Старый вопрос, но я предлагаю свое решение для полноты картины. Мне это было нужно, когда мне нужна была конкретная находка, но другое создание, если его не существует.
def self.find_by_or_create_with(args, attributes) # READ CAREFULLY! args for finding, attributes for creating! obj = self.find_or_initialize_by(args) return obj if obj.persisted? return obj if obj.update_attributes(attributes) end
источник
Вы можете сделать это одним оператором, например так:
CachedObject.where(key: "the given key").first_or_create! do |cached| cached.attribute1 = 'attribute value' cached.attribute2 = 'attribute value' end
источник
Сиквел камень добавляет update_or_create метод , который , кажется, делать то , что вы ищете.
источник