find_or_create_by in Rails 3 and updating for creating records

前端 未结 3 755
执念已碎
执念已碎 2020-12-05 01:17

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 d

相关标签:
3条回答
  • 2020-12-05 01:52

    Here is two approaches.

    First you can extend Available with exact method you need:

    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
    

    usage

    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
    

    This is awkward. So for being more flexible you can create update_or_create_by... method for ActiveRecord using 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
    

    So now you can use it:

    Available.update_or_create_by_id_and_source(20, "my_source") do |a|
      a.whatever = "coooool"
    end
    
    0 讨论(0)
  • 2020-12-05 01:53

    I think the simplest way is using Ruby's tap method, like this:

    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 finds or intitializes a record, then returns it. We then tap into it, make our updates and save it to the database.

    0 讨论(0)
  • 2020-12-05 02:00

    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.

    0 讨论(0)
提交回复
热议问题