在Rails 3中查找_or_create_by并更新以创建记录

我不确定我是否应该以这种方式更新记录,或者我是否遗漏了某些内容。

我有一个包含5列(不包括时间戳和id)的表,其中3个是不同的,2个将更新。 我将找到或创建的3个不同的是room_id,date和source。 另外2个是可用的价格和点数(这些每小时,每天等变化)

我的问题是,如果我首先找到或创建记录,然后更新(或创建)价格和点数,或者我可以一次完成所有操作吗? 你现在可以看到我正在做的两种方式,而且我不确定它是否真的在做我期待的事情。

另外,像这样做find_and_create_by有什么缺点吗?

谢谢

private def self.parse_data(params,data) data.beds.each do |bed| room = Room.find_or_create_room(bed.title, params[:id]) #find clones somehow #puts bed.nights.first.price bed.nights.each_with_index do |night,index| available = Available.find_or_create_by_room_id_and_bookdate_and_source( :room_id => room.id, :bookdate => (params[:date].to_date)+index, :source => data.class.to_s#, #:price => night.price ) #available.price = night.price #available.spots = night.spots #available.save end end 

这是两种方法。

首先,您可以使用您需要的精确方法扩展Available

 def self.find_or_create_by_room_id_and_bookdate_and_source(room_id, bookdate, source, &block) obj = self.find_by_room_id_and_bookdate_and_source( room_id, bookdate, source ) || self.new(:room_id => room_id, :bookdate => bookdate, :source => source) yield obj obj.save end 

用法

 Available.find_or_create_by_room_id_and_bookdate_and_source(room.id, (params[:date].to_date)+index, data.class.to_s) do |c| c.price = night.price c.spots = night.spots end 

这很尴尬。 因此,为了更灵活,您可以使用method_missing magic为ActiveRecord创建update_or_create_by...方法:

 class ActiveRecord::Base def self.method_missing(method_id, *args, &block) method_name = method_id.to_s if method_name =~ /^update_or_create_by_(.+)$/ update_or_create($1, *args, &block) else super end end def self.update_or_create(search, *args, &block) parameters = search.split("_and_") params = Hash[ parameters.zip(args) ] obj = where(params).first || self.new(params) yield obj obj.save obj end end 

所以现在你可以使用它:

 Available.update_or_create_by_id_and_source(20, "my_source") do |a| a.whatever = "coooool" end 

实际上,有一种没有任何黑客攻击的方式。 您可以使用find_or_initialize_by而不是find_or_create_by,并使用tap设置更新的属性

 Available.find_or_initialize_by_room_id_and_bookdate_and_source( room.id, (params[:date].to_date)+index, data.class.to_s# ).tap do |a| a.price = night.price a.spots = night.spots end.save! 

最初这看起来很混乱,但它正是你所要求的。 查找记录,如果找不到则更新它并更新属性。 这可以被称为“find_and_update_or_create_by”,幸运的是没有人这样做。 ;)希望这个帮助。