find_or_create_by в Rails 3 и обновление для создания записей
Я не уверен, должен ли я обновлять записи таким образом или я что-то упускаю.
У меня есть таблица с 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
2 ответа
Вот два подхода.
Сначала вы можете продлить 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
На самом деле, есть способ без какого-либо взлома. Вместо find_or_create_by вы можете использовать find_or_initialize_by и устанавливать обновленные атрибуты нажатием
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", к счастью, никто этого не сделал.;) Надеюсь, это поможет.
Я думаю, что самый простой способ - использовать метод Tap Ruby, например:
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
находит или инициализирует запись, а затем возвращает ее. Затем мы подключаемся к нему, вносим обновления и сохраняем их в базе данных.