【问题标题】:How can I permanently ignore a database column in my ActiveRecord::Base class?如何永久忽略 ActiveRecord::Base 类中的数据库列?
【发布时间】:2015-07-26 09:01:56
【问题描述】:

我有一个旧数据库,我正在尝试使用 Rails 对其进行建模。其中一个表有一个名为 attributes 的列,我认为这是 Rails 保留的名称。

这是表的 SQL:

CREATE TABLE `album` (
  `id` int(11) NOT NULL,
  `artist` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `gid` char(36) NOT NULL,
  `modpending` int(11) DEFAULT '0',
  `attributes` int(11) DEFAULT '0',
  ...
);

这是我的 ActiveRecord 类:

class Album < ActiveRecord::Base
  set_table_name "album"
  belongs_to :artist
  has_many :tracks, :through => :album_tracks
end

当我尝试实例化一个实例时会发生以下情况:

hornairs@bishop:~/Sites/logdb (master *)$ rails c
Loading development environment (Rails 3.0.3)
no such file to load -- irbtools
ruby-1.9.2-p0 > x = Album.find_by_name("Champ")
 => #<Album id: 969139, artist: 354493, name: "Champ", gid: "15a9a4b8-9dd9-4f6f-b4e9-7c69948af88f", modpending: 0, attributes: 1100, page: 143735328, language: 120, script: 28, modpending_lang: nil, quality: -1, modpending_qual: 0> 
ruby-1.9.2-p0 > x.name
ActiveRecord::DangerousAttributeError: attributes_before_type_cast is defined by ActiveRecord
  from /Users/hornairs/.rvm/gems/ruby-1.9.2-p0@logdb/gems/activerecord-3.0.3/lib/active_record/attribute_methods.rb:23:in `instance_method_already_implemented?'
  from /Users/hornairs/.rvm/gems/ruby-1.9.2-p0@logdb/gems/activemodel-3.0.3/lib/active_model/attribute_methods.rb:263:in `block (2 levels) in define_attribute_methods'
  from /Users/hornairs/.rvm/gems/ruby-1.9.2-p0@logdb/gems/activemodel-3.0.3/lib/active_model/attribute_methods.rb:262:in `each'
  from /Users/hornairs/.rvm/gems/ruby-1.9.2-p0@logdb/gems/activemodel-3.0.3/lib/active_model/attribute_methods.rb:262:in `block in define_attribute_methods'
  from /Users/hornairs/.rvm/gems/ruby-1.9.2-p0@logdb/gems/activemodel-3.0.3/lib/active_model/attribute_methods.rb:261:in `each'
  from /Users/hornairs/.rvm/gems/ruby-1.9.2-p0@logdb/gems/activemodel-3.0.3/lib/active_model/attribute_methods.rb:261:in `define_attribute_methods'
  from /Users/hornairs/.rvm/gems/ruby-1.9.2-p0@logdb/gems/activerecord-3.0.3/lib/active_record/attribute_methods.rb:13:in `define_attribute_methods'
  from /Users/hornairs/.rvm/gems/ruby-1.9.2-p0@logdb/gems/activerecord-3.0.3/lib/active_record/attribute_methods.rb:41:in `method_missing'
  from /Users/hornairs/.rvm/gems/ruby-1.9.2-p0@logdb/gems/thwart-0.0.4/lib/thwart/canable.rb:27:in `method_missing'
  from (irb):2
  from /Users/hornairs/.rvm/gems/ruby-1.9.2-p0@logdb/gems/railties-3.0.3/lib/rails/commands/console.rb:44:in `start'
  from /Users/hornairs/.rvm/gems/ruby-1.9.2-p0@logdb/gems/railties-3.0.3/lib/rails/commands/console.rb:8:in `start'
  from /Users/hornairs/.rvm/gems/ruby-1.9.2-p0@logdb/gems/railties-3.0.3/lib/rails/commands.rb:23:in `<top (required)>'
  from script/rails:6:in `require'
  from script/rails:6:in `<main>'
ruby-1.9.2-p0 > 

看起来attributes 名称似乎是保留的,所以我想找到某种方法在所有查询中忽略它,并让 AR 在反映模式以定义模型类时忽略它。有什么建议?谢谢!

【问题讨论】:

  • 我不知道解决方案,但我找到了一个有同样问题的人的帖子:railsforum.com/viewtopic.php?id=41399>。不确定它会有所帮助。

标签: ruby-on-rails ruby-on-rails-3 activerecord


【解决方案1】:

结合使用 Robin 的链接和其他一些 SO 答案中的内容解决了这个问题

class Album < ActiveRecord::Base
  set_table_name "album"

  class << self
    def instance_method_already_implemented?(method_name)
      return true if method_name =~ /^attributes/
      super
    end
  end

  belongs_to :artist
  has_many :tracks, :through => :album_tracks
end

成功了。我对以attributes 开头的所有方法都进行了彻底的更改以返回true,而不会引发错误,而且我认为它不会在其他地方造成任何问题。

【讨论】:

    【解决方案2】:
      class << self
         def instance_method_already_implemented?(method_name)
          return true if method_name =~ /^attributes/
          super
        end
      end
    

    这个补丁很好,主要是为我工作,但是当你检查类似的东西时 Album.column_names 和 Album.columns.collect(&:name) 你仍然会得到所有的列。 这也会失败。

     a = Album.last
     a = Album.new(a.attributes) 
    

    要让 rails 完全忽略列,您可以这样做。

     class Album < ActiveRecord::Base
        set_table_name "album"
        ## --------------------- Ignore columns patch ------
        @@ignore_column_pattern = /^column_one|^column_two/
    
        class << self
          alias :all_columns :columns
          def columns 
            @columns_filt ||= all_columns.reject { |col| col.name =~ @@ignore_column_pattern } 
          end 
        end
    
        alias :all_attribute_names :attribute_names
        def attribute_names
          @attr_names_filt ||= all_attribute_names.reject { |att| att =~ @@ignore_column_pattern }
        end
        ## ------------------- / Ignore columns patch ------
        belongs_to :artist
        has_many :tracks, :through => :album_tracks
      end
    

    我还使用了一个数组来存储我不想要的列,但您仍然可以使用正则表达式的方式!现在,您可以了解该列的唯一方法是使用连接,即。

    Album.connection.columns("albums").collect(&:name)
    

    【讨论】:

      【解决方案3】:

      (这是一个老问题,但在谷歌中仍然存在,所以我会添加一个迟来的答案)

      我发现限制由 ActiveRecord 模型加载的数据的最佳方法是创建一个仅包含您希望加载的列的数据库视图。您可以使用 ActiveRecord 的 table_name 方法将 ActiveRecord 模型指向受约束的视图。原生 SQL 工具仍然可以操作基础表,但 ActiveRecord 只会看到(并因此加载)显式包含在视图中的列。

      【讨论】:

        猜你喜欢
        • 2010-10-07
        • 1970-01-01
        • 2014-11-24
        • 2014-01-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多