Подтвердить что ты не робот

ActiveRecord: обновить запись, если она существует, создать?

Попытка реализовать запись создания, если она не существует, в Active Record.

В настоящее время используется:

@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!

Каким будет правильный способ обновления записи, если она существует, или создать ее?

4b9b3361

Ответ 2

В Rails 4

Student.
  find_or_initialize_by(:user_id => current_user.id).
  update_attributes!(:department => 1)

Ответ 3

::first_or_create (доступный начиная с версии v.2.2.1) делает то, что он говорит в поле.

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">

Если вы также хотите обновить уже существующую запись, попробуйте:

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: согласно Doug comment, если ваши проверки сбой между вызовами #create и #update или вы хотите минимизировать вызовы базы данных, вместо этого вы можете использовать ::first_or_initialize, чтобы избежать сохранения записи при первом вызове. Тем не менее, вы должны, чтобы впоследствии вы вызывали #save или #update, чтобы сохранить запись:

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. Существует метод под названием #create_or_update, но не обманывайтесь какой-либо документацией, которую вы можете найти в Google; это только частный метод, используемый #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

Это красивее, если у вас есть связь между пользователем и учеником. Что-то вдоль линий

@student = current_user.student || current_user.build_student
@student.department_id = 1
@student.save

EDIT:

Вы также можете использовать http://guides.rubyonrails.org/active_record_querying.html#first_or_create, как было указано семьсекатом, но вам все равно придется иметь дело с различными сценариями, такими как обновление идентификатора студенческого отдела.

UPDATE:

Вы можете использовать find_or_create_by

@student = Student.find_or_create_by(user_id: current_user.id) do |student|
  student.department_id = 1
end

Ответ 5

К сожалению, я думаю, что самый чистый способ сделать это:

Student.where(user_id: id).first_or_create(age: 16).update_attribute(:age, 16)

Ответ 6

Это find_or_create_by не first_or_create.
Пример: Client.find_or_create_by (first_name: 'Andy')