我希望能够在数据库中创建一条记录,但是阻止Rails从那时起进行更改。我知道在数据库级别仍然可以进行更改。
我相信attr_readonly在属性级别上做我想要的,但我不想手动指定字段...我宁愿有更多的白名单方法。
另外,我知道有一个:read_only选项用于关联,但我不想限制对象的“readonlyness”,如果它是通过关联获取的。
最后,我希望能够销毁记录,例如:dependent => :破坏协会的工作。
因此,总结一下:1)允许创建记录,2)允许删除记录,3)防止更改已保留的记录。
答案 0 :(得分:74)
查看ActiveRecord::Persistence
,所有内容最终都会在幕后调用create_or_update
。
def create_or_update
raise ReadOnlyRecord if readonly?
result = new_record? ? create : update
result != false
end
原来如此!只是:
def readonly?
!new_record?
end
答案 1 :(得分:47)
我找到了一个更简洁的解决方案,它使用after_initialize
回调:
class Post < ActiveRecord::Base
after_initialize :readonly!
end
答案 2 :(得分:25)
为什么不在数据库上创建具有只读访问权限的用户,并让rails使用该帐户。
但是,如果您需要模型级访问权限,可以将以下内容添加到特定模型中:
def readonly?
true
end
def before_destroy
raise ActiveRecord::ReadOnlyRecord
end
答案 3 :(得分:16)
此博客文章仍然有效:http://ariejan.net/2008/08/17/activerecord-read-only-models/
如果添加方法,基本上可以依赖ActiveRecord的验证:
def readonly?
true
end
答案 4 :(得分:5)
class YourModel < ActiveRecord::Base
before_save { false } # prevent create & update, allows destroy
# ...
end
before_create { false }
before_update { false }
before_destroy { false } # does not prevent delete
另请参阅:http://guides.rubyonrails.org/active_record_callbacks.html
答案 5 :(得分:2)
这似乎相当有效,可能有点矫枉过正,但对于我的情况,我真的想确保我的应用程序永远不会创建,保存,更新或销毁模型中的任何记录。
module ReadOnlyModel
def readonly?() true end
def create_or_update() raise ActiveRecord::ReadOnlyRecord end
before_create { raise ActiveRecord::ReadOnlyRecord }
before_destroy { raise ActiveRecord::ReadOnlyRecord }
before_save { raise ActiveRecord::ReadOnlyRecord }
before_update { raise ActiveRecord::ReadOnlyRecord }
end
class MyModel < ActiveRecord::Base
include ReadOnlyModel
# ...
end
由于OP要求能够创建和销毁但不保存或更新我相信这将起作用
module SaveAndDestroyOnlyModel
before_save { raise ActiveRecord::ReadOnlyRecord }
before_update { raise ActiveRecord::ReadOnlyRecord }
end
class MyModel < ActiveRecord::Base
include SaveAndDestroyOnlyModel
# ...
end
不是完全正确的异常,但我认为足够接近。
答案 6 :(得分:0)
自定义验证器可以执行此操作:
validate :nothing_changed, unless: :new_record? # make immutable
...
def nothing_changed
errors.add(:base, "Record is read-only") if self.changed?
end
答案 7 :(得分:0)
寻找一种方法来实现@Nate提出的相同控制(避免任何类型的create / update / delete),但只能在我的应用程序的特定部分以及所有模型中一次使用此方法,而我已经创建了Ruby {{ 3}}:
module ReadOnlyRailsMode
CLASS_METHODS = ActiveRecord::Base.methods
.select { |m| m =~ /(update|create|destroy|delete|save)[^\?]*$/ }
INSTANCE_METHODS = ActiveRecord::Base.instance_methods
.select { |m| m =~ /(update|create|destroy|delete|save)[^\?]*$/ }
refine ActiveRecord::Base.singleton_class do
CLASS_METHODS.each do |m|
define_method(m) do |*args|
raise ActiveRecord::ReadOnlyRecord
end
end
end
refine ActiveRecord::Base do
def readonly?; true; end
INSTANCE_METHODS.each do |m|
define_method(m) do |*args|
raise ActiveRecord::ReadOnlyRecord
end
end
end
end
并仅在代码的特定部分使用它:
class MyCoolMailerPreview < ActionMailer::Preview
using ReadOnlyRailsMode
end
(这是一个真实的用例,我一直在寻找一种方法来避免人们从ActionMailer :: Previews内部创建和编辑真实记录,因为我想允许在生产中进行预览,但是如果有人错误地创建了可以更改的预览,真实数据,这会造成混乱)。
代码很难重新定义所有方法(创建,创建!等),因为其目的是改变所有模型的行为,并且“ before_create”之类的回调不能用于此目的,因为它们不会仅在本地使用“使用”范围,从而更改整个应用程序。
这种方法对我有用,我可以在一个类中为所有模型显式阻止所有这些方法,并且不要与应用程序的其余部分搞混。不幸的是,到目前为止,改进还不适用于子类,因此在我的情况下,默认情况下我无法阻止所有插入父类(ActionMailer :: Preview)的插入,这是我的最初目标,但每个类的阻止是一个很好的起点。
我的应用程序需要完善所有方法,但是可以仅对一些有趣的方法(如destroy或update)进行控制,这对所有情况都适用,包括原始问题中的一种。
答案 8 :(得分:0)
.default_scope
似乎也有效,不过上面的一些答案可能会更好,因为可以通过使用 .unscoped
来规避这种方式。不过,这可能对某些人的需求有益。
使用 Postgres 制作临时只读模型来查询 pg_type
表的示例。
Class.new(ActiveRecord::Base) { self.table_name = 'pg_type'; self.primary_key = :oid; default_scope { readonly } }.first.readonly?
# => true