ActiveRecord: Update a record if exists else create?

后端 未结 7 1672
北荒
北荒 2020-12-30 19:04

Trying to implement an create if not exists else update record in Active Record.

Currently using:

@student = Student.where(:user_id          


        
相关标签:
7条回答
  • 2020-12-30 19:43

    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

    0 讨论(0)
  • 2020-12-30 19:43
    @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
    
    0 讨论(0)
  • 2020-12-30 19:44

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

    0 讨论(0)
  • 2020-12-30 19:45

    In Rails 4

    Student.
      find_or_initialize_by(:user_id => current_user.id).
      update_attributes!(:department => 1)
    
    0 讨论(0)
  • 2020-12-30 19:48

    Unfortunately I think the cleanest way to do this is:

    Student.where(user_id: id).first_or_create(age: 16).update_attribute(:age, 16)
    
    0 讨论(0)
  • 2020-12-30 19:54

    It's find_or_create_by not first_or_create.
    Ex: Client.find_or_create_by(first_name: 'Andy')

    0 讨论(0)
提交回复
热议问题