find_or_create_by in Rails 3 and updating for creating records

holden picture holden · Apr 7, 2011 · Viewed 28.2k times · Source

I'm not sure if I should be updating records this way or if I'm missing something.

I have a table with 5 columns (not including timestamps and id) 3 of which are distinct, and 2 which will get updated. The 3 distinct which I will find or create by are room_id, date, and source. The other 2 are price and spots available (these change hourly, daily etc.)

My question is, should I first find or create the record, then update (or create) the price and spots or can I do it all at once? You can see the two ways I'm doing it now, and I'm not sure if its actually doing what I'm expecting.

Also, is there any downside to do a find_and_create_by like this?

Thanks

  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

Answer

Tian Chen picture Tian Chen · Jul 10, 2012

Actually, there is a way without any hacking. Instead of find_or_create_by you can use find_or_initialize_by and set updated atributes with 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!

Initially this can seems cluttered, but it is doing exactly what you asked for. Find the record, instanciate it if not found and update atributes. this could be called "find_and_update_or_create_by", fortunatelly nobody did that. ;) Hope this help.