I have a class called
CachedObject that stores generic serialised objects indexed by key. I want this class to implement a
create_or_update method. If an object is found it will update it, otherwise it will create a new one.
Is there a way to do this in Rails or do I have to write my own method?
This question is related to
Rails 6 added an
upsert_all methods that deliver this functionality.
[upsert] It does not instantiate any models nor does it trigger Active Record callbacks or validations.
Not if you are looking for an "upsert" (where the database executes an update or an insert statement in the same operation) type of statement. Out of the box, Rails and ActiveRecord have no such feature. You can use the upsert gem, however.
Otherwise, you can use:
find_or_create_by, which offer similar functionality, albeit at the cost of an additional database hit, which, in most cases, is hardly an issue at all. So unless you have serious performance concerns, I would not use the gem.
For example, if no user is found with the name "Roger", a new user instance is instantiated with its
name set to "Roger".
user = User.where(name: "Roger").first_or_initialize user.email = "[email protected]" user.save
Alternatively, you can use
user = User.find_or_initialize_by(name: "Roger")
In Rails 3.
user = User.find_or_initialize_by_name("Roger") user.email = "[email protected]" user.save
You can use a block, but the block only runs if the record is new.
User.where(name: "Roger").first_or_initialize do |user| # this won't run if a user with name "Roger" is found user.save end User.find_or_initialize_by(name: "Roger") do |user| # this also won't run if a user with name "Roger" is found user.save end
If you want to use a block regardless of the record's persistence, use
tap on the result:
User.where(name: "Roger").first_or_initialize.tap do |user| user.email = "[email protected]" user.save end