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.
'IT TIP' 카테고리의 다른 글
지금부터 5 초 후 Java로 어떻게 말합니까? (0) | 2020.12.13 |
---|---|
Python에서 JSON 출력 정렬 (0) | 2020.12.13 |
AndroidManifest.xml이 누락되었습니다. (0) | 2020.12.13 |
PHP 날짜는 현재 날짜에 5 년 추가 (0) | 2020.12.13 |
npm start가 포트 8000에서 서버를 실행하는 방법 (0) | 2020.12.13 |