问题
Trying to implement an create if not exists else update record in Active Record.
Currently using:
@student = Student.where(:user_id => current_user.id).first
if @student
Student.destroy_all(:user_id => current_user.id)
end
Student = Student.new(:user_id => current_user.id, :department => 1
)
Student.save!
What would be the correct way of updating the record if it exists or else create it?
回答1:
You may be looking for first_or_create
or something similar:
http://guides.rubyonrails.org/v3.2.17/active_record_querying.html#first_or_create
回答2:
In Rails 4
Student.
find_or_initialize_by(:user_id => current_user.id).
update_attributes!(:department => 1)
回答3:
::first_or_create
(available since v3.2.1) does what it says on the box.
Model.where(find: 'find_value').
first_or_create(create: 'create_value')
# If a record with {find: 'find_value'} already exists:
before #=> #<Model id: 1, find: "find_value", create: nil>
after #=> #<Model id: 1, find: "find_value", create: nil>
# Otherwise:
before #=> nil
after #=> #<Model id: 2, find: "find_value", create: "create_value">
If you also want it to update an already-existing record, try:
Model.where(find: 'find_value').
first_or_create(create: 'create_value').
update(update: 'update_value')
# If one already exists:
before #=> #<Model id: 1, find: "find_value", create: nil, update: nil>
after #=> #<Model id: 1, find: "find_value", create: nil, update: "update_value">
# If it already matches, no UPDATE statement will be run:
before #=> #<Model id: 1, find: "find_value", create: nil, update: "update_value">
after #=> #<Model id: 1, find: "find_value", create: nil, update: "update_value">
# Otherwise:
before #=> nil
after #=> #<Model id: 2, find: "find_value", create: 'create_value', update: "update_value">
EDIT 2016-03-08: As per Doug's comment, if your validations fail between the #create
and #update
calls, or you want to minimise database calls, you can use ::first_or_initialize
instead, to avoid persisting the record on the first call. However, you must make sure you call #save
or #update
afterwards in order to persist the record (and I'm not sure if #update
works on records that haven't been persisted yet):
Model.validates :update, presence: true # The create call would fail this
Model.where(find: 'find_value').
first_or_initialize(create: 'create_value'). # doesn't call validations
update(update: 'update_value')
(NB. There is a method called #create_or_update
, but don't be fooled by any documentation you may find on Google; that's just a private method used by #save
.)
回答4:
@student = Student.where(user_id: current_user.id).first
@student ||= Student.new(user_id: current_user.id)
@student.department_id = 1
@student.save
This is prettier if you have an association between a user and a student. Something along the lines of
@student = current_user.student || current_user.build_student
@student.department_id = 1
@student.save
EDIT:
You can also use http://guides.rubyonrails.org/active_record_querying.html#first_or_create as answered by sevenseacat but you still have to deal with different scenarios like update a student's department id.
UPDATE:
You can use find_or_create_by
@student = Student.find_or_create_by(user_id: current_user.id) do |student|
student.department_id = 1
end
回答5:
In Rails 5
Student.
find_or_initialize_by(:user_id => current_user.id).
update(:department => 1)
(with all credit taken from @Zorayr answer).
回答6:
Unfortunately I think the cleanest way to do this is:
Student.where(user_id: id).first_or_create(age: 16).update_attribute(:age, 16)
回答7:
It's find_or_create_by not first_or_create.
Ex:
Client.find_or_create_by(first_name: 'Andy')
来源:https://stackoverflow.com/questions/14599113/activerecord-update-a-record-if-exists-else-create