Ruby-on-rails 有没有一种简单的方法可以使 Rails ActiveRecord 模型只读?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5641410/
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
Is there an easy way to make a Rails ActiveRecord model read-only?
提问by brettish
I want to be able to create a record in the DB but then prevent Rails from making changes from that point on. I understand changes will still be possible at the DB level.
我希望能够在数据库中创建一条记录,但随后阻止 Rails 从那时起进行更改。我知道在 DB 级别仍然可以进行更改。
I believe attr_readonly does what I want on an attribute level, but I don't want to have to manually specify fields... I would rather have more of a white-list approach.
我相信 attr_readonly 在属性级别上做了我想要的,但我不想手动指定字段......我宁愿有更多的白名单方法。
Also, I know there is a :read_only option for associations, but I don't want to limit the "readonlyness" of the object to if it was fetched via an association or not.
另外,我知道关联有一个 :read_only 选项,但我不想将对象的“只读”限制为是否通过关联获取。
Finally, I want to be able to still destroy a record so stuff like :dependent => :destroy works in the associations.
最后,我希望仍然能够销毁记录,因此 :dependent => :destroy 之类的东西在关联中起作用。
So, to summarize: 1) allow the creation of records, 2) allow the deletion of records, and 3) prevent changing records that have been persisted.
因此,总结一下:1) 允许创建记录,2) 允许删除记录,以及 3) 防止更改已保存的记录。
回答by scragz
Looking at ActiveRecord::Persistence, everything ends up calling create_or_updatebehind the scenes.
看着ActiveRecord::Persistence,一切最终都create_or_update在幕后呼唤。
def create_or_update
raise ReadOnlyRecord if readonly?
result = new_record? ? create : update
result != false
end
So! Just:
所以!只是:
def readonly?
!new_record?
end
回答by kwarrick
I've found a more concise solution, which uses the after_initializecallback:
我找到了一个更简洁的解决方案,它使用after_initialize回调:
class Post < ActiveRecord::Base
after_initialize :readonly!
end
回答by Mike Lewis
Why not just create a user on the database that has read only access, and have rails use that account.
为什么不直接在数据库上创建一个具有只读访问权限的用户,并让 rails 使用该帐户。
However if you want model level access, you can add the following to a specific model:
但是,如果您想要模型级别的访问权限,您可以将以下内容添加到特定模型:
def readonly?
true
end
def before_destroy
raise ActiveRecord::ReadOnlyRecord
end
回答by apneadiving
This blog post is still valid: http://ariejan.net/2008/08/17/activerecord-read-only-models/
这篇博文仍然有效:http: //ariejan.net/2008/08/17/activerecord-read-only-models/
Basically you can rely on ActiveRecord's validation if you add a method:
如果添加方法,基本上您可以依赖 ActiveRecord 的验证:
def readonly?
true
end
回答by apneadiving
TL;DR for OP's
TL; OP 的 DR
class YourModel < ActiveRecord::Base
before_save { false } # prevent create & update, allows destroy
# ...
end
Generally
一般来说
- To prevent creates only:
before_create { false } - To prevent updates only:
before_update { false } - To prevent destroys only:
before_destroy { false } # does not prevent delete
- 仅防止创建:
before_create { false } - 仅阻止更新:
before_update { false } - 仅防止破坏:
before_destroy { false } # does not prevent delete
See also: http://guides.rubyonrails.org/active_record_callbacks.html
另见:http: //guides.rubyonrails.org/active_record_callbacks.html
回答by Nate
This seems to be fairly effective and is probably a bit overkill, but for my case, I really want to be sure my application will never create, save, update, or destroy any records in the model, ever.
这似乎相当有效,而且可能有点矫枉过正,但就我而言,我真的想确保我的应用程序永远不会创建、保存、更新或销毁模型中的任何记录。
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
Since OP asked to be able to create and destroy but not save or update I believe this will work
由于 OP 要求能够创建和销毁但不能保存或更新我相信这会起作用
module SaveAndDestroyOnlyModel
before_save { raise ActiveRecord::ReadOnlyRecord }
before_update { raise ActiveRecord::ReadOnlyRecord }
end
class MyModel < ActiveRecord::Base
include SaveAndDestroyOnlyModel
# ...
end
Not exactlythe right exception, but close enough I think.
不完全是正确的例外,但我认为足够接近。
回答by Burnztheitroade
A custom validator can do this:
自定义验证器可以这样做:
validate :nothing_changed, unless: :new_record? # make immutable
...
def nothing_changed
errors.add(:base, "Record is read-only") if self.changed?
end
回答by Everton J. Carpes
Looking for a way to achieve the same control proposed by @Nate (avoiding any kind of create/update/delete) but using this only in specific parts of my application and for all models at once I have created this Ruby refinement:
寻找一种方法来实现@Nate 提出的相同控制(避免任何类型的创建/更新/删除),但仅在我的应用程序的特定部分和所有模型中使用它,我已经创建了这个 Ruby改进:
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
And to use it only in a specific portion of the code:
并且仅在代码的特定部分使用它:
class MyCoolMailerPreview < ActionMailer::Preview
using ReadOnlyRailsMode
end
(This is a real use case, I was looking for a way to avoid people creating and editing real records from inside ActionMailer::Previews because I want to allow previews in production, but if by mistake anyone creates a preview which changes real data, this would became a chaos).
(这是一个真实的用例,我一直在寻找一种方法来避免人们从 ActionMailer::Previews 内部创建和编辑真实记录,因为我希望允许在生产中进行预览,但是如果有人错误地创建了更改真实数据的预览,这将成为混乱)。
The code is a little ugly redefining all methods (create, create!, etc) because the intent is to change the behavior of all models, and callbacks like "before_create" can't be used for this purpose since they would not be locally only to the "using" scope, changing the whole application.
重新定义所有方法(create、create! 等)的代码有点丑陋,因为其目的是更改所有模型的行为,而像“before_create”这样的回调不能用于此目的,因为它们不会仅在本地到“使用”范围,改变整个应用程序。
This approach is working for me, I can explicitly block all this methods for all models in just one class, and don't mess with the rest of the application. Unfortunately, until now, refinements don't apply to sub classes, so in my case I was not able to block all inserts by default into the parent class (ActionMailer::Preview), which was my original goal, but blocking per class is a good starting point.
这种方法对我有用,我可以在一个类中为所有模型显式阻止所有这些方法,并且不会干扰应用程序的其余部分。不幸的是,直到现在,改进还不适用于子类,所以在我的情况下,我无法默认阻止所有插入到父类 (ActionMailer::Preview) 中,这是我最初的目标,但每个类的阻塞是一个很好的起点。
My application requires refining all methods, but the control can be done for just the interesting methods like destroy, or update and them this can works for all cases, including the one from the original question.
我的应用程序需要改进所有方法,但可以只对有趣的方法(如销毁或更新)进行控制,这适用于所有情况,包括原始问题中的方法。

