В Rails лучший способ обновить запись или создать новую, если она не существует?
У меня есть оператор 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
? Я не уверен, какой атрибут я найду, поскольку модель Im look at - это модель объединения, которая имеет только id
внешние ключи и атрибут оценки.
Я пробовал
@user.choices.find_or_create_by_user(:user => @user.id, :interest => interest, :score => 4)
но получил
undefined метод find_by_user
Что мне делать?
Ответы
Ответ 1
Предполагая, что модель 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
Некоторые примечания:
- Вам не нужно включать столбец
user_id
в find_or_*_by_*
, поскольку вы уже указали Rails только на выбор choices
, принадлежащий @user
.
- Я использую
find_or_initialize_by_*
, который по существу те же, что и find_or_create_by_*
, причем одним ключевым отличием является то, что initialize
фактически не создает запись. Это было бы похоже на Model.new
в отличие от Model.create
.
- Блок, который устанавливает
c.score = 0
, выполняется только в том случае, если запись не существует.
-
choice.score += 1
обновит значение оценки для записи, независимо от того, существует ли она или нет. Следовательно, значением по умолчанию c.score = 0
должно быть начальное значение минус единица.
- Наконец,
choice.save!
либо обновит запись (если она уже существует), либо создаст начальную запись (если это не так).
Ответ 2
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
})
Ответ 3
find_or_create_by_user_id
звучит лучше
Ответ 4
Кроме того, в Rails 3 вы можете:
@user.choices.where(:user => @user.id, :interest => interest, :score => 4).first_or_create
Ответ 5
Если вы используете рельсы 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
Ответ 6
В 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))