【问题标题】:How to add a has_many association on all models如何在所有模型上添加 has_many 关联
【发布时间】:2010-04-22 13:45:12
【问题描述】:

现在我有一个初始化器可以做到这一点:

ActiveRecord::Base.send :has_many, :notes, :as => :notable ActiveRecord::Base.send :accepts_nested_attributes_for, :notes

它可以很好地建立关联,除非我加载使用它的视图时,第二次加载给了我: can't dup NilClass 来自:

/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2184:in `dup'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2184:in `scoped_methods'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2188:in `current_scoped_methods'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2171:in `scoped?'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2439:in `send'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2439:in `initialize'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/reflection.rb:162:in `new'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/reflection.rb:162:in `build_association'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations/association_collection.rb:423:in `build_record'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations/association_collection.rb:102:in `build'
(my app)/controllers/manifests_controller.rb:21:in `show'

有什么想法吗?我这样做是错误的吗?有趣的是,如果我将关联移动到我目前正在使用的模型上,我不会收到此错误。我想我一定是错误地建立了全局关联。

【问题讨论】:

  • 您是否有任何理由不直接在模型中指定此关联?
  • 我有很多很多模型,我希望它们都有注释。 DRY 原则说要把它放到它们都继承的类中。
  • 顺便说一句,我做了更多的研究,发现问题只存在于开发中,Rails 会在每个请求上重新加载类。看来 Rails 没有重新加载 ActiveRecord::Base,在这种情况下我想要它。

标签: ruby-on-rails ruby activerecord polymorphic-associations


【解决方案1】:

您说您有许多模型,所有模型都需要这种关联。如果是我,我会采用创建包含关联的基本模型类的方法,然后让所有其他模型从它继承。比如:

class NotableModel < ActiveRecord::Base

  # Prevents ActiveRecord from looking for a database table for this class
  self.abstract_class = true

  has_many :notes, :as => :notable
  accepts_nested_attributes_for :notes  
end

class Foo < NotableModel
  ...
end

class Bar < NotableModel
  ...
end

在我看来,与使用隐藏在初始化程序中的一点元编程相比,这种方法更具自我记录性。

【讨论】:

  • 我同意 - 在基类或包含的模块中明确定义。
  • 这个方法有效,但是它改变了继承链。一般来说没问题,但我喜欢 DRYer 方法
  • 这是干燥的;你认为重复了什么?
  • @joshsz,这很干,比打鸭子 ActiveRecord::Base 好得多
  • 特别是因为这个新的基类可以使用其他方法或属性进行扩展,这些方法或属性可能对您的所有模型都有用。
【解决方案2】:

看看unloadable可能对你有帮助

【讨论】:

  • 那篇博文很有趣,但并没有解决我的问题。请参阅我的 cmets 关于为什么的最初问题:)
【解决方案3】:

建议在每个模型中进行每个关联!这是一种无用的 DRY 方法来制作这样的东西!总之,这是我的意见!

【讨论】:

    【解决方案4】:

    感谢 Rich Kilmer(InfoEther 的),我们找到了解决此问题的优雅(且略显不透明)的方法:

    # config/initializers/has_many_notes.rb
    module ActiveRecord
      class Base
        def self.inherited(klass)
          super
          klass.send :has_many, :notes, :as => :notable
          klass.send :accepts_nested_attributes_for, :notes
        end
      end
    end
    

    现在没有继承变化,而且非常干燥

    【讨论】:

    • @joshsz,很可能,您会在某个时候遇到不想成为Notable 的模型。这不亚于@John Topley 的方法 DRY。另外,如果您对 DRY 如此执着,您还应该遵守 Convention Over Configuration。你的方法是神秘的,非自我记录的。
    • DRY 不是你在做什么。当前每个模型都继承自 ActiveRecord::Base。让它从 NotableModel 继承根本不会增加任何重复,它的数量完全相同。将这个与模型相关的代码从模型文件夹中移开一点也不直观。它绝对有效,不要误会我的意思,但是考虑到其他人正在接近您的代码,我将不得不进行一些搜索才能找到它。继承的存在正是为了解决这类问题。继承应该优先于猴子补丁。
    • 这是本项目的正确解决方案,但仅出于以下原因: 1) 本项目还有其他以相同方式处理的完整代码库横切关注点(例如事件)。 2) 该领域的原则规定一切 必须始终引人注目,甚至(可能)注释。我们是否显示每个模型的功能是稍后做出的决定,但仍有可能。 3) 扩展核心类并不少见。如果你认为这是一个核心扩展,它突然变得不那么奇怪了。
    猜你喜欢
    • 2016-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多