IT TIP

Rails ActiveRecord 모델을 읽기 전용으로 만드는 쉬운 방법이 있습니까?

itqueen 2020. 12. 13. 11:38
반응형

Rails ActiveRecord 모델을 읽기 전용으로 만드는 쉬운 방법이 있습니까?


DB에 레코드를 생성 할 수 있기를 원하지만 Rails가 그 시점부터 변경하지 못하도록합니다. DB 수준에서 변경이 가능하다는 것을 이해합니다.

나는 attr_readonly가 내가 원하는 것을 속성 수준에서 수행한다고 믿지만, 수동으로 필드를 지정할 필요는 없습니다. 오히려 화이트리스트 접근 방식을 더 선호합니다.

또한 연관에 대한 : read_only 옵션이 있다는 것을 알고 있지만 연관을 통해 가져 왔는지 여부에 따라 객체의 "읽기 전용"을 제한하고 싶지 않습니다.

마지막으로, 나는 여전히 레코드를 파괴 할 수 있기를 원하므로 : dependent => : destroy와 같은 것들이 연관에서 작동합니다.

요약하면, 1) 레코드 생성 허용, 2) 레코드 삭제 허용, 3) 지속 된 레코드 변경 방지.


를 보면 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

after_initialize콜백 을 사용하는 더 간결한 솔루션을 찾았습니다 .

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

읽기 전용 액세스 권한이 있고 레일이 해당 계정을 사용하도록 데이터베이스에 사용자를 생성하지 않는 이유는 무엇입니까?

그러나 모델 수준 액세스를 원하는 경우 특정 모델에 다음을 추가 할 수 있습니다.

 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

OP에 대한 TL; DR

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가 제안한 동일한 제어를 달성하는 방법을 찾고 있지만 (모든 종류의 생성 / 업데이트 / 삭제를 피함) 내 애플리케이션의 특정 부분에서만 사용하고 모든 모델에 대해 한 번에이 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

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

참고URL : https://stackoverflow.com/questions/5641410/is-there-an-easy-way-to-make-a-rails-activerecord-model-read-only

반응형