Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ActiveRecord: Update a record if exists else create?

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?

like image 744
Rubytastic Avatar asked Jan 30 '13 07:01

Rubytastic


3 Answers

In Rails 4

Student.
  find_or_initialize_by(:user_id => current_user.id).
  update_attributes!(:department => 1)
like image 197
Zorayr Avatar answered Nov 08 '22 16:11

Zorayr


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

like image 29
sevenseacat Avatar answered Nov 08 '22 16:11

sevenseacat


::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.)

like image 15
PJSCopeland Avatar answered Nov 08 '22 17:11

PJSCopeland