在 Rails 中,更新记录或创建新记录(如果不存在)的最佳方法是啥?

Posted

技术标签:

【中文标题】在 Rails 中,更新记录或创建新记录(如果不存在)的最佳方法是啥?【英文标题】:In Rails, what is the best way to update a record or create a new one if it doesn't exist?在 Rails 中,更新记录或创建新记录(如果不存在)的最佳方法是什么? 【发布时间】:2011-06-05 00:09:29 【问题描述】:

我对某些模型有一个 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

我该怎么办?

【问题讨论】:

current_user 已经是 User 模型的实例了吗?如果是这样,您不需要重新找到它,只需使用current_user 而不是@user = ... 【参考方案1】:

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

【讨论】:

【参考方案2】:

此外,在 Rails 3 中,您可以:

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

【讨论】:

first_or_create 在 Rails 4 中是首选,同时 find_or_intialize_by_xxx(attribute) 已被弃用,取而代之的是 find_or_intialize_by(xxx:attribute)【参考方案3】:

如果您使用的是 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

【讨论】:

【参考方案4】:

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

一些注意事项:

    您不需要在find_or_*_by_* 中包含user_id 列,因为您已经指示Rails 只获取属于@userchoices。 我使用的是find_or_initialize_by_*,它与find_or_create_by_* 基本相同,唯一的区别是initialize 实际上并不创建记录。这类似于Model.new,而不是Model.create。 设置c.score = 0 的块仅在记录存在时才会执行。 choice.score += 1 将更新记录的分数值,无论它是否存在。因此,默认分数c.score = 0 应该是初始值减一。 最后,choice.save! 将更新记录(如果已存在)或创建已启动的记录(如果不存在)。

【讨论】:

链接这个类似的问题***.com/questions/18747062/… 这个答案应该更新,有些方法不适合rails 4【参考方案5】:
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
)

【讨论】:

不知道为什么这被否决了。这对我很有帮助 - 感谢您的贡献! 我发现它也很有帮助。我猜不赞成票是因为它只部分解决了 OP 的要求。【参考方案6】:

find_or_create_by_user_id 听起来更好

【讨论】:

有什么区别? 这不更新记录?它找到它,或者它创建它。

以上是关于在 Rails 中,更新记录或创建新记录(如果不存在)的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Rails:创建/更新记录时出现 EOF 错误

创建/更新记录时EOF错误:轨

为啥 Rails 在通过关联创建新记录时不更新其关联缓存?

更新方法不更新rails 4中的记录

shell脚本之对不存的文件进行记录

复杂Rails Active Record查询选择具有真实结果的记录,以便当天最新更新其他创建的记录