Ruby-on-rails 在 Rails 中,如果记录不存在,更新记录或创建新记录的最佳方法是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4621208/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 00:01:52  来源:igfitidea点击:

In Rails, what is the best way to update a record or create a new one if it doesn't exist?

ruby-on-railsconditional-statements

提问by ChrisWesAllen

I have a create statement for some models, but it's creating a record within a join table regardless of whether the record already exists.

我有一些模型的 create 语句,但无论记录是否已经存在,它都会在连接表中创建一条记录。

Here is what my code looks like:

这是我的代码的样子:

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

The problem is that it creates records no matter what. I would like it to create a record only if no record already exists; if a record does exist, I would like it to take the attribute of the found record and add or subtract 1.

问题是它无论如何都会创建记录。我希望它仅在没有记录存在时才创建记录;如果确实存在记录,我希望它采用找到的记录的属性并加或减 1。

I've been looking around have seen something called find_or_create_by. What does this do when it finds a record? I would like it to take the current :scoreattribute and add 1.

我一直环顾四周,看到了一个叫做find_or_create_by. 当它找到一条记录时,它会做什么?我希望它采用当前:score属性并添加 1。

Is it possible to find or create by id? I'm not sure what attribute I would find by, since the model I'm looking at is a join model which only has idforeign keys and the score attribute.

是否可以通过查找或创建id?我不确定我会通过什么属性找到,因为我正在查看的模型是一个连接模型,它只有id外键和 score 属性。

I tried

我试过

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

but got

但得到

undefined method find_by_user

未定义的方法 find_by_user

What should I do?

我该怎么办?

采纳答案by vonconrad

Assuming that the Choicemodel has a user_id(to associate with a user) and an interest_id(to associate with an interest), something like this should do the trick:

假设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

Some notes:

一些注意事项:

  1. You don't need to include the user_idcolumn in the find_or_*_by_*, as you've already instructed Rails to only fetch choicesbelonging to @user.
  2. I'm using find_or_initialize_by_*, which is essentially the same as find_or_create_by_*, with the one key difference being that initializedoesn't actually create the record. This would be similar to Model.newas opposed to Model.create.
  3. The block that sets c.score = 0is only executed if the record does notexist.
  4. choice.score += 1will update the score value for the record, regardless if it exists or not. Hence, the default score c.score = 0should be the initial value minus one.
  5. Finally, choice.save!will either update the record (if it already existed) or create the initiated record (if it didn't).
  1. 您不需要在 中包含该user_idfind_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!将更新记录(如果它已经存在)或创建启动的记录(如果它不存在)。

回答by krunal shah

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
})

回答by apneadiving

find_or_create_by_user_idsounds better

find_or_create_by_user_id听起来更好

回答by Zuhaib Ali

Also, in Rails 3you can do:

此外,在Rails 3 中,您可以执行以下操作:

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

回答by Albert.Qing

In Rails 4You can use find_or_create_byto get an object(if not exist,it will create), then use updateto save or update the record, the update method will persist record if it is not exist, otherwise update record.

在 Rails 4 中你可以find_or_create_by用来获取一个对象(如果不存在,它会创建),然后update用来保存或更新记录,如果记录不存在,update 方法会持久化记录,否则更新记录。

For example

例如

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

回答by rainkinz

If you're using rails 4 I don't think it creates the finder methods like it used to, so find_or_create_by_user isn't created for you. Instead you'd do it like this:

如果您使用的是 rails 4,我认为它不会像以前那样创建 finder 方法,因此不会为您创建 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