如何检查内存中记录的数据库更改?

时间:2014-07-29 10:06:50

标签: ruby-on-rails

我想检查ActiveRecord实例是否已按数据库方式更改。类似的东西:

p1 = Product.first
p1.name #=> "some name"

p2 = Product.first
p2.name = "other name"
p2.save #=> true

p1.database_changed? #=> true

我目前正在将记录的属性与持久属性进行比较:

class Product < ActiveRecord::Base

  def database_changed?
    Product.find(id).attributes != attributes
  end

end

这似乎有效,但我想知道是否有内置的方法来查找数据库更改?

3 个答案:

答案 0 :(得分:3)

Зелёный's comment之后我审核了ActiveModel::Dirty,并意识到它几乎可以实现我的目标。已经存在内存状态(记录的属性)和数据库状态(由ActiveModel::Dirty处理)。我只需要一种方法来更新数据库状态,保持内存状态不变:

def refresh
  @changed_attributes = {}
  fresh_object = self.class.unscoped { self.class.find(id) }
  fresh_object.attributes.each do |attr, orig_value|
    @changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, @attributes[attr])
  end
  self
end
  • @changed_attributesActiveModel::Dirty的哈希值,用于存储更改的值。我们显然必须重置它。
  • fresh_object是从数据库中新获取的相同记录(this line来自reload,感谢emaillenin)。
  • 在循环内,将每个(新鲜)属性与相应的(内存中)属性进行比较。如果它们不同,则会将其添加到@changed_attributes哈希。 This line来自ActiveRecord的{​​{1}}方法。 (它实际上来自dup调用的私有方法,而dup也是私有的。使用_field_changed的公共API可能更好,但我很懒。)
  • 最后,为方便起见,ActiveRecord返回refresh,就像self一样。

以下是一个示例用法:

reload

答案 1 :(得分:1)

Rails的方法是在ActiveRecord对象上使用reload方法。

  def database_changed?
    attributes != reload.attributes
  end

此新代码repo中提供了此代码。

1号航站楼

2.1.2 :001 > c = Car.find(1)
  Car Load (0.4ms)  SELECT  "cars".* FROM "cars"  WHERE "cars"."id" = ? LIMIT 1  [["id", 1]]
 => #<Car id: 1, name: "Audi", model: "R8", created_at: "2014-07-29 11:14:43", updated_at: "2014-07-29 11:14:43">
2.1.2 :002 > c.database_changed?
  Car Load (0.1ms)  SELECT  "cars".* FROM "cars"  WHERE "cars"."id" = ? LIMIT 1  [["id", 1]]
 => false
2.1.2 :003 > c.database_changed?
  Car Load (0.2ms)  SELECT  "cars".* FROM "cars"  WHERE "cars"."id" = ? LIMIT 1  [["id", 1]]
 => false
2.1.2 :004 > c.database_changed?
  Car Load (0.2ms)  SELECT  "cars".* FROM "cars"  WHERE "cars"."id" = ? LIMIT 1  [["id", 1]]
 => true

2号航站楼

2.1.2 :001 > c = Car.find(1)
  Car Load (0.2ms)  SELECT  "cars".* FROM "cars"  WHERE "cars"."id" = ? LIMIT 1  [["id", 1]]
 => #<Car id: 1, name: "Audi", model: "R8", created_at: "2014-07-29 11:14:43", updated_at: "2014-07-29 11:14:43">
2.1.2 :002 > c.model = 'A5'
 => "A5"
2.1.2 :003 > c.save!
   (0.2ms)  begin transaction
  SQL (0.3ms)  UPDATE "cars" SET "model" = ?, "updated_at" = ? WHERE "cars"."id" = 1  [["model", "A5"], ["updated_at", "2014-07-29 11:15:32.845875"]]
   (1.2ms)  commit transaction
 => true
2.1.2 :004 >

答案 2 :(得分:1)

def database_changed?
  self.class.where(self.class.arel_table[:updated_at].gt(updated_at)).exists? self
end