Is there an easy way to make a Rails ActiveRecord model read-only?

后端 未结 8 1280
-上瘾入骨i
-上瘾入骨i 2021-01-31 01:25

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.

相关标签:
8条回答
  • 2021-01-31 02:08

    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.

    0 讨论(0)
  • 2021-01-31 02:12

    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
    
    0 讨论(0)
提交回复
热议问题