有没有一种简单的方法可以使 Rails ActiveRecord 模型成为只读?

发布于 2024-11-01 09:19:12 字数 297 浏览 1 评论 0原文

我希望能够在数据库中创建一条记录,但随后阻止 Rails 从那时起进行更改。我了解数据库级别的更改仍然是可能的。

我相信 attr_readonly 在属性级别上做了我想要的事情,但我不想手动指定字段......我宁愿有更多的白名单方法。

另外,我知道关联有一个 :read_only 选项,但我不想将对象的“只读性”限制为是否通过关联获取。

最后,我希望仍然能够销毁记录,例如 :dependent =>; :销毁协会中的作品。

因此,总结一下:1)允许创建记录,2)允许删除记录,3)防止更改已保存的记录。

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.

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.

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.

Finally, I want to be able to still destroy a record so stuff like :dependent => :destroy works in the associations.

So, to summarize: 1) allow the creation of records, 2) allow the deletion of records, and 3) prevent changing records that have been persisted.

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

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

发布评论

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

评论(9

明天过后 2024-11-08 09:19:13

查看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

Looking at ActiveRecord::Persistence, everything ends up calling create_or_update behind the scenes.

def create_or_update
  raise ReadOnlyRecord if readonly?
  result = new_record? ? create : update
  result != false
end

So! Just:

def readonly?
  !new_record?
end
や莫失莫忘 2024-11-08 09:19:13

我找到了一个更简洁的解决方案,它使用 after_initialize 回调:

class Post < ActiveRecord::Base
  after_initialize :readonly!
end

I've found a more concise solution, which uses the after_initialize callback:

class Post < ActiveRecord::Base
  after_initialize :readonly!
end
痕至 2024-11-08 09:19:13

为什么不在数据库上创建一个具有只读访问权限的用户,并让 Rails 使用该帐户。

但是,如果您想要模型级别访问权限,您可以将以下内容添加到特定模型:

 def readonly?
    true
  end

  def before_destroy
    raise ActiveRecord::ReadOnlyRecord
  end

Why not just create a user on the database that has read only access, and have rails use that account.

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
几味少女 2024-11-08 09:19:13

这篇博文仍然有效:http://ariejan.net/2008 /08/17/activerecord-read-only-models/

基本上,如果添加一个方法,您就可以依赖 ActiveRecord 的验证:

def readonly?
  true
end

This blog post is still valid: http://ariejan.net/2008/08/17/activerecord-read-only-models/

Basically you can rely on ActiveRecord's validation if you add a method:

def readonly?
  true
end
半步萧音过轻尘 2024-11-08 09:19:13

TL;DR OP 的

class YourModel < ActiveRecord::Base
  before_save { false } # prevent create & update, allows destroy

  # ... 
end

一般情况

  • 仅防止创建: before_create { false }
  • 仅防止更新: before_update { false }
  • 仅防止销毁: before_destroy { false } # 不会阻止删除

另请参阅:http://guides.rubyonrails.org/active_record_callbacks.html

TL;DR for OP's

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

See also: http://guides.rubyonrails.org/active_record_callbacks.html

破晓 2024-11-08 09:19:13

这似乎相当有效,并且可能有点矫枉过正,但就我而言,我真的想确保我的应用程序永远不会创建、保存、更新或销毁模型中的任何记录。

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

虽然不完全是正确的例外,但我认为足够接近。

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

module SaveAndDestroyOnlyModel
  before_save { raise ActiveRecord::ReadOnlyRecord }
  before_update { raise ActiveRecord::ReadOnlyRecord }
end

class MyModel < ActiveRecord::Base
  include SaveAndDestroyOnlyModel
  # ...
end

Not exactly the right exception, but close enough I think.

万劫不复 2024-11-08 09:19:13

自定义验证器可以执行以下操作:

validate :nothing_changed, unless: :new_record? # make immutable

...

def nothing_changed
  errors.add(:base, "Record is read-only") if self.changed?
end

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
我很坚强 2024-11-08 09:19:13

寻找一种方法来实现 @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

并仅在特定部分使用它代码:(

class MyCoolMailerPreview < ActionMailer::Preview
  using ReadOnlyRailsMode 
end

这是一个真实的用例,我一直在寻找一种方法来避免人们从 ActionMailer::Previews 内部创建和编辑真实记录,因为我想允许在生产中进行预览,但是如果有人错误地创建了一个改变真实记录的预览数据,这会变得混乱)。

重新定义所有方法(create、create!等)的代码有点难看,因为其目的是更改所有模型的行为,并且像“before_create”这样的回调不能用于此目的,因为它们不仅仅在本地到“使用”范围,改变整个应用程序。

这种方法对我有用,我可以在一个类中显式阻止所有模型的所有这些方法,并且不会干扰应用程序的其余部分。不幸的是,到目前为止,细化不适用于子类,因此在我的情况下,我无法默认阻止所有插入到父类(ActionMailer::Preview)中,这是我最初的目标,但每个类的阻止是一个好的起点。

我的应用程序需要改进所有方法,但是可以仅对诸如销毁或更新之类的有趣方法进行控制,并且这可以适用于所有情况,包括原始问题中的情况。

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:

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).

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.

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.

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.

无需解释 2024-11-08 09:19:13

.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

.default_scope seems to also work, though some of the above answers may be better as this way could be circumvented by using .unscoped. Though, that may be beneficial for some peoples needs.

Example with Postgres making a temporary, read only model to query the pg_type table.

Class.new(ActiveRecord::Base) { self.table_name = 'pg_type'; self.primary_key = :oid; default_scope { readonly } }.first.readonly?
# => true
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文