有没有一种简单的方法可以将Rails ActiveRecord模型设置为只读?

问题描述

| 我希望能够在数据库中创建一条记录,但是从那以后阻止Rails进行更改。我知道在数据库级别仍然可以进行更改。 我相信attr_readonly可以满足我在属性级别上的要求,但是我不想手动指定字段...我宁愿拥有更多的白名单方法。 另外,我知道关联有一个:read_only选项,但我不想将对象的“ readonlyness”限制为是否通过关联获取。 最后,我希望仍然可以销毁记录,以便诸如:depend =>:destroy之类的东西在关联中起作用。 因此,总结一下:1)允许创建记录,2)允许删除记录,和3)防止更改已保留的记录。     

解决方法

看着
ActiveRecord::Persistence
,一切都在幕后叫calling1ѭ。
def create_or_update
  raise ReadOnlyRecord if readonly?
  result = new_record? ? create : update
  result != false
end
所以!只是:
def readonly?
  !new_record?
end
    ,我找到了一个更简洁的解决方案,它使用
after_initialize
回调:
class Post < ActiveRecord::Base
  after_initialize :readonly!
end
    ,为什么不只在数据库上创建一个具有只读访问权限并且使Rails使用该帐户的用户。 但是,如果要模型级别的访问,可以将以下内容添加到特定模型:
 def readonly?
    true
  end

  def before_destroy
    raise ActiveRecord::ReadOnlyRecord
  end
    ,该博客文章仍然有效:http://ariejan.net/2008/08/17/activerecord-read-only-models/ 基本上,如果添加方法,则可以依赖ActiveRecord的验证:
def readonly?
  true
end
    ,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 } # does not prevent delete
另请参阅:http://guides.rubyonrails.org/active_record_callbacks.html     ,这似乎是相当有效的,可能有点过大了,但是对于我而言,我真的想确保我的应用程序永远不会创建,保存,更新或销毁模型中的任何记录。
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
不是完全正确的例外,但我认为足够接近。     ,自定义验证器可以执行以下操作:
validate :nothing_changed,unless: :new_record? # make immutable

...

def nothing_changed
  errors.add(:base,\"Record is read-only\") if self.changed?
end
    ,寻找一种方法来实现@Nate提出的相同控制(避免任何类型的create / update / delete),但仅在我的应用程序的特定部分以及所有模型中一次使用此控件,就创建了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内部创建和编辑真实记录的方法,因为我想允许在生产中进行预览,但是如果有人错误地创建了可以更改实际数据的预览,这会变得混乱)。 该代码有点丑陋地重新定义了所有方法(创建,创建!等),因为目的是要更改所有模型的行为,并且像这样的回调不能使用“ before_create \”,因为它们不会仅在“使用”范围内本地更改整个应用程序。 这种方法对我有用,我可以在一个类中为所有模型显式地阻止所有这些方法,并且不会与应用程序的其余部分混淆。不幸的是,到目前为止,改进还不适用于子类,因此在我的情况下,默认情况下我无法阻止所有插入父类(ActionMailer :: Preview)的插入,这是我的最初目标,但每个类均会阻止是一个很好的起点。 我的应用程序需要完善所有方法,但是可以仅对一些有趣的方法(如destroy或update)进行控制,并且这些方法可以适用于所有情况,包括原始问题中的一种。     

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...