我不确定我是否应该以这种方式更新记录,或者我是否遗漏了某些内容。
我有一个包含5列(不包括时间戳和id)的表,其中3个是不同的,2个将更新。我将找到或创建的3个不同的是room_id,date和source。另外两个是可用的价格和点(这些每小时,每天等变化)
我的问题是,我应该首先找到或创建记录,然后更新(或创建)价格和点数,还是我可以一次完成所有操作?你现在可以看到我正在做的两种方式,而且我不确定它是否真的在做我期待的事情。
另外,像这样做一个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
答案 0 :(得分:23)
实际上,有一种方法没有任何黑客行为。 您可以使用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”,幸运的是没有人这样做。 ;) 希望这有帮助。
答案 1 :(得分:15)
这是两种方法。
First您可以使用您需要的确切方法扩展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
这很尴尬。因此,为了更灵活,您可以使用update_or_create_by...
魔法为ActiveRecord
创建method_missing
方法:
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
答案 2 :(得分:0)
我认为最简单的方法是使用Ruby的tap方法,如下所示:
def self.parse_data(params,data)
data.beds.each do |bed|
room = Room.find_or_create_room(bed.title, params[:id])
bed.nights.each_with_index do |night,index|
Available.find_or_initialize_by(room_id: room.id).tap do |available|
available.bookdate = (params[:date].to_date) + index
available.source = data.class.to_s
available.price = night.price
available.save
end
end
end
end
find_or_initialize_by
查找或初始化记录,然后将其返回。然后,我们点击它,进行更新并将其保存到数据库中。