我想要一个模型,如果我对其进行更改并保存,它将另存为新记录。这就像不可变字符串的想法。我想知道是否有涵盖此领域的现有 gem/教程/最佳实践?
我看过的大多数宝石只是阻止你进行任何修改。但是我想修改,只是将修改保存到记录的副本中。从理论上讲,这应该涵盖 Rails 中的任何更新方式,例如 update_attributes
,并且save
也应该返回新的记录副本。
我还没有听说过特定的任务宝石,但在早期的 Rails 中,有只读记录,并且尝试更新该记录被例外地引用。在现代版本的 Rails 中,这种记录已被弃用,并且没有可以为关系设置只读属性。但是,您可以创建一个拒绝更新记录的模型,但在控制器中,您可以捕获该特定异常,并尝试基于拒绝的记录创建新记录,如下所示:
class ReadonlyModelException < ActiveRecord::ActiveRecordError; end
class ReadonlyModel < ActiveRecord::Base
before_update { |_| raise ReadonlyModelException }
end
class User < ReadonlyModel; end
在控制器中,只需捕获异常并更改行为。
def update
user = User.where(id: params[:id]).first
user.update_attributes(name: "New name") # => ReadonlyModelException
rescue ReadonlyModelException
redirect :create
end
注意:但在我看来,更好的方法是不要对特定对型号/控制器使用 #update
操作,而只对#new/#create
。
您可能可以使用 rails changed?
方法。基于User
模型的示例:
def update
@object = YourClass.find(params[:id])
if @object.changed?
# Create new object
else
# Action if no change has been made
end
end
changed?
方法将检查模型的任何属性是否已更改,您希望指定要使用的确切属性object.attributename_changed?
:
[10] pry(main)> u = User.last
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."type" IN ('User', 'AnonymousUser', 'DeletedUser') ORDER BY "users"."id" DESC LIMIT 1
=> #<User id: 5, login: "Carson725", firstname: "Hillard", lastname: "VonRueden">
[11] pry(main)> u.lastname = 'Someother'
=> "Sergiu"
[12] pry(main)> u.changed?
=> true
[13] pry(main)> u.firstname_changed?
=> false
[14] pry(main)> u.lastname_changed?
=> true
[15] pry(main)>
红宝石 2.1.5p273/导轨 3.2.21
http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
所以最后我把以下内容放到我的模型中。
为了简单起见,我通过将内置保存函数设置为只读来阻止对内置保存函数的访问。
然后我有一个保存修改的方法。将保存副本,但会更新对该副本的选定引用。例如,当我更新价格对象时,我还使用该价格更新现有产品以指向新的价格记录。虽然订单仍将引用旧记录。
def readonly?
persisted?
end
def save_as_clone
return self if ! changed?
copy = self.dup
ActiveRecord::Base.transaction do
copy.save!
if copy.persisted?
# Update existing references
self.products.update_all(price_id:copy.id)
end
end
copy
end
def destroy
# remove mapping only but do not destroy self
end
def delete
# remove mapping only but do not destroy self
end
我想我可以通过覆盖 save/save!/update_attributes 方法来使其更加隐式。但是,由于这不是正常行为,我选择明确并停在这里。如果有人有更通用的解决方案,也请发布。