find_or_create_by в Rails 3 и обновление для создания записей - PullRequest
9 голосов
/ 07 апреля 2011

Я не уверен, должен ли я обновлять записи таким образом или я что-то упускаю.

У меня есть таблица с 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 ]

23 голосов
/ 10 июля 2012

На самом деле, есть способ без какого-либо взлома.Вместо 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", к счастью, никто этого не сделал.;) Надеюсь, что это поможет.

15 голосов
/ 07 апреля 2011

Вот два подхода.

Первый Вы можете расширить 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 magic:

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
...