Rails 3 中的 find_or_create_by 和更新以创建记录

发布于 2024-10-30 19:06:16 字数 955 浏览 5 评论 0原文

我不确定我是否应该以这种方式更新记录,或者我是否遗漏了一些东西。

我有一个包含 5 列(不包括时间戳和 id)的表,其中 3 列是不同的,2 列将更新。我将查找或创建的 3 个不同属性是 room_id、日期和来源。另外 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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

遮云壑 2024-11-06 19:06:16

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

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

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.

仲春光 2024-11-06 19:06:16

这里有两种方法。

首先您可以使用您需要的确切方法扩展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

这很尴尬。因此,为了更加灵活,您可以使用 method_missing 魔法为 ActiveRecord 创建 update_or_create_by... 方法:

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

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
千秋岁 2024-11-06 19:06:16

我认为最简单的方法是使用 Ruby 的 tap< /a> 方法,如下所示:

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 查找或初始化一条记录,然后返回它。然后我们利用它,进行更新并将其保存到数据库中。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文