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

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

这是我的代码的样子:

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

问题是无论如何都会创建记录。 我希望只有在没有记录的情况下才能创建记录; 如果记录确实存在,我希望它采用找到的记录的属性并加1或减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

我该怎么办?

假设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. 您不需要在find_or_*_by_*包含user_id列,因为您已经指示Rails仅提取属于@user choices
  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应该是初始值减1。
  5. 最后, choice.save! 将更新记录(如果它已经存在)或创建启动的记录(如果它没有)。
 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 }) 

find_or_create_by_user_id听起来更好

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

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

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

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