Ruby-on-rails Rails 3 中的 find_or_create_by 和更新以创建记录

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5578625/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 00:52:11  来源:igfitidea点击:

find_or_create_by in Rails 3 and updating for creating records

ruby-on-railsruby-on-rails-3activerecorddynamic-finders

提问by holden

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

我有一个包含 5 列(不包括时间戳和 ID)的表,其中 3 个是不同的,2 个将得到更新。我将找到或创建的 3 个不同是 room_id、date 和 source。其他 2 个是价格和可用位置(这些每小时、每天等变化)

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?

另外,像这样执行 find_and_create_by 有什么缺点吗?

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

回答by Tian Chen

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

实际上,有一种没有任何黑客攻击的方法。您可以使用 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!

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.

最初这看起来很杂乱,但它完全按照你的要求做。查找记录,如果未找到则将其实例化并更新属性。这可以称为“find_and_update_or_create_by”,幸运的是没有人这样做。;) 希望这有帮助。

回答by fl00r

Here is two approaches.

这里有两种方法。

Firstyou can extend Availablewith exact method you need:

首先,您可以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

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 ActiveRecordusing method_missingmagic:

这很尴尬。因此,为了更加灵活,您可以创建使用魔法的update_or_create_by...方法:ActiveRecordmethod_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(, *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