Rails create or update magic?
Rails 6
Rails 6 added an upsert
and upsert_all
methods that deliver this functionality.
Model.upsert(column_name: value)
[upsert] It does not instantiate any models nor does it trigger Active Record callbacks or validations.
Rails 5, 4, and 3
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_initialize_by
or 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@example.com"
user.save
Alternatively, you can use find_or_initialize_by
.
user = User.find_or_initialize_by(name: "Roger")
In Rails 3.
user = User.find_or_initialize_by_name("Roger")
user.email = "email@example.com"
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@example.com"
user.save
end
create_or_update method in rails
my_class = ClassName.find_or_initialize_by_name(name)
my_class.update_attributes(
:street_address => self.street_address,
:city_name => self.city_name,
:federalid => self.federalid,
:state_prov_id => self.state_prov_id,
:zip_code => self.zip_code
)
As of Rails 6, update_attributes!
and update_attributes
is deprecated for update!
and update
, respectively:
my_class.update(
:street_address => self.street_address,
:city_name => self.city_name,
:federalid => self.federalid,
:state_prov_id => self.state_prov_id,
:zip_code => self.zip_code
)
Create or update object in table
This is the solution:
person = Person.find_or_create_by(firstname: row["firstname"])
person.update_attributes({firstname: row['firstname'], lastname: row['lastname'], home_phone_number: row['home_phone_number'], mobile_phone_number: row['mobile_phone_number'], address: row['address'], email: row['email']})
I can't explain why this solution is working and not the one you proposed to me.
But thanks @sohail_khalil and @abhilash for your help !
Updating if exist or create if not rails
Firstly, the model.Model
part should be just Model
, as Model
is your class.
locked
is supposed to be a column/attribute of the Model class, although it seems is not the case judging from your error. Therefore, I'm gonna use other_column_name
as an example.
Explanation of what this is doing:
Model.where(column_name: value).first_or_initialize(other_column_name: some_value)
Model.where(column_name: value)
: gets models that satisfy the condition column_name == value
first_or_initialize
: if a model such that column_name == value was found, that one is returned. Otherwise, it initializes a model with column_name = value.
By passing other_column_name: some_value
, if the model was not found and needs to be initialized, it sets other_column_name
to some_value
but: 1) it does not update it if it was initially found and 2) it does not save the record.
The equivalent of first_or_initialize
that saves the new record would be first_or_create
but this would still not update the record if it already existed.
So, you should do something like this:
m = Model.where(column_name: value).first_or_initialize
m.other_column_name = some_value
m.save
This way, you first get a model where column_name
is value
or initialize a new one with this value if it didn't already exist. Then, you set the attribute other_column_name
to some_value
and save the model.
A one-liner alternative would be
Model.where(column_name: value).first_or_create.update(other_column_name: some_value)
However, note that if it needs to be created, this one will perform 2 queries (the insert and the update).
About the error part. It says the attribute locked does not exist on the Model record. Are these classes you created? Are you using some pre-existing project? You could try posting Model.attribute_names
and maybe your schema.rb
Rails Activerecord: create or update after HTTP POST method
You want to use PUT for update and POST for create. If you know the user exists, but you don't have the id, you would use a PUT request, and in the update method, you would have something like this:
def update
@user = User.find_by_ip(params[:ip])
if @user.update_attributes(...)
#success
else
#failure
end
end
Again, the ideal method would be to pass the id into the update, but if you are unable to do that, you don't want to sacrifice RESTful conventions.
Ruby/Rails worker to create or update record if exists - avoid DRY
Take a look at find_or_initialize_by method:
class StoreActivityWorker
include Sidekiq::Worker
def perform(webhook)
entity = webhook.dig 'entity'
activity = Activity.find_or_initialize_by(cms_activity_id: entity['id'])
activity.update!(
is_separate_activity: entity.dig('attributes', 'is_separate_activity'),
content_full: retrieve_full_content(webhook),
content_basic: retrieve_basic_content(webhook),
)
end
end
What is the correct HTTP method for a create or update action in a Rails API?
IMHO, most appropriate would be PUT. See HTTP 1.1, section 4.3.4 PUT, which is a "create or replace".
Rails models not being set on create or update
You're creating accessor methods which impede access to the methods ActiveRecord already generates for you.
Your definition should be:
class UnsupportedInstitution < ActiveRecord::Base
validates :bank_name, presence: true
validates :reason, presence: true
has_many :unsupported_institution_routing_numbers
end
Where bank_name
will be auto-generated as a method if there is a corresponding field in the database, which there is if your migration ran successfully.
Related Topics
How to Check for File Existence
Parsing Xls and Xlsx (Ms Excel) Files with Ruby
How (Replace|Create) an Enum Field on Rails 2.0 Migrations
Macos Mojave 'Ruby/Config.H' File Not Found
Rails - the System Cannot Find the Path Specified
Rvm Is Not a Function, Selecting Rubies with 'Rvm Use ...' Will Not Work
How to Know What Is Not Thread-Safe in Ruby
Devise Logged in Root Route Rails 3
How to Find Out Which Gem Has a Specific Dependency
Base64 Photo and Paperclip -Rails
Is There Goto Statement in Ruby
Using Factory_Girl in Rails with Associations That Have Unique Constraints. Getting Duplicate Errors
Rails Hidden Field Undefined Method 'Merge' Error
Add Http(S) to Url If It's Not There
How to Embed Regular Expressions in Other Regular Expressions in Ruby