В Rails, как лучше всего обновить запись или создать новую, если она не существует? - PullRequest
26 голосов
/ 07 января 2011

У меня есть оператор create для некоторых моделей, но он создает запись в таблице соединений независимо от того, существует ли запись.

Вот как выглядит мой код:

@user = User.find(current_user)
@event = Event.find(params[:id])
for interest in @event.interests
 @user.choices.create(:interest => interest, :score => 4)
end

Проблема в том, что он создает записи независимо от того, что. Я хотел бы создать запись только в том случае, если запись еще не существует; если запись существует, я бы хотел взять атрибут найденной записи и добавить или вычесть 1.

Я осматривался, видел что-то под названием find_or_create_by. Что это делает, когда находит запись? Я хотел бы взять текущий атрибут :score и добавить 1.

Можно ли найти или создать id? Я не уверен, по какому атрибуту я бы нашел, поскольку рассматриваемая модель - это модель соединения, которая имеет только внешние ключи id и атрибут Score.

Я пытался

@user.choices.find_or_create_by_user(:user => @user.id, :interest => interest, :score => 4)

но получил

неопределенный метод find_by_user

Что мне делать?

Ответы [ 6 ]

60 голосов
/ 07 января 2011
my_class = ClassName.find_or_initialize_by_name(name)

my_class.update_attributes({
   :street_address => self.street_address,
   :city_name => self.city_name,
   :zip_code => self.zip_code
})
23 голосов
/ 07 января 2011

Предполагая, что модель Choice имеет user_id (для связи с пользователем) и interest_id (для связи с интересом), что-то вроде этого должно сработать:

@user = User.find(current_user)
@event = Event.find(params[:id])

@event.interests.each do |interest|
  choice = @user.choices.find_or_initialize_by_interest_id(interest.id) do |c|
    c.score = 0 # Or whatever you want the initial value to be - 1
  end

  choice.score += 1
  choice.save!
end

Некоторые примечания:

  1. Вам не нужно включать столбец user_id в find_or_*_by_*, так как вы уже указали Rails выбирать только choices, принадлежащий @user.
  2. Я использую find_or_initialize_by_*, что по сути то же самое, что и find_or_create_by_*, с единственным отличием в том, что initialize на самом деле не создает запись.Это будет похоже на Model.new в отличие от Model.create.
  3. Блок, который устанавливает c.score = 0, выполняется, только если запись не существует.
  4. choice.score += 1 обновит значение оценки для записи, независимо от того, существует она или нет.Следовательно, значение по умолчанию c.score = 0 должно быть начальным значением минус один.
  5. Наконец, choice.save! либо обновит запись (если она уже существовала), либо создаст инициированную запись (если она не была).
7 голосов
/ 07 января 2011

find_or_create_by_user_id звучит лучше

3 голосов
/ 01 февраля 2014

Также в Rails 3 вы можете сделать:

@user.choices.where(:user => @user.id, :interest => interest, :score => 4).first_or_create
0 голосов
/ 27 сентября 2016

В Rails 4 Вы можете использовать find_or_create_by, чтобы получить объект (если он не существует, он создаст), затем используйте update, чтобы сохранить или обновить запись, метод обновления сохранит записьесли он не существует, в противном случае обновите запись.

Например

@edu = current_user.member_edu_basics.find_or_create_by(params.require(:member).permit(:school))

if @edu.update(params.require(:member).permit(:school, :majoy, :started, :ended))
0 голосов
/ 31 декабря 2013

Если вы используете rails 4, я не думаю, что он создает методы поиска, как раньше, поэтому find_or_create_by_user не создан для вас.Вместо этого вы бы сделали это так:

@user = User.find(current_user)
@event = Event.find(params[:id])
for interest in @event.interests
 @user.choices.find_or_create_by(:interest => interest) do |c|
  c.score ||= 0
  c.score += 1
 end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...