【问题标题】:Rails: Overriding ActiveRecord association methodRails:覆盖 ActiveRecord 关联方法
【发布时间】:2011-02-22 20:41:29
【问题描述】:

有没有办法覆盖 ActiveRecord 关联提供的方法之一?

例如,我有以下典型的多态 has_many :通过关联:

class Story < ActiveRecord::Base
    has_many :taggings, :as => :taggable
    has_many :tags, :through => :taggings, :order => :name
end


class Tag < ActiveRecord::Base
    has_many :taggings, :dependent => :destroy
    has_many :stories, :through => :taggings, :source => :taggable, :source_type => "Story"
end

您可能知道,这会为 Story 模型添加大量相关方法,例如标签、标签

如何覆盖这些方法之一?特别是 tags

def tags<< *new_tags
    #do stuff
end

调用时会产生语法错误,所以显然没那么简单。

【问题讨论】:

  • 你这样做是为了什么?这最终可能会破坏其他 ActiveRecord 功能,并且可能有更好的方法来做你正在尝试的事情

标签: ruby-on-rails ruby activerecord overriding


【解决方案1】:

您可以使用带有has_many 的块来扩展您与方法的关联。请参阅评论“使用块扩展您的关联”here
覆盖现有方法也可以,不过不知道是不是个好主意。

  has_many :tags, :through => :taggings, :order => :name do
    def << (value)
      "overriden" #your code here
      super value
    end     
  end

【讨论】:

  • 当然!忘了那个。这可能是您追求的最佳方式。
  • 你会如何调用原始方法? (我想重写构建方法,添加一些默认值然后调用原来的)
  • 我怎样才能以这种方式覆盖关联 getter?
  • :-|他们本可以使用 RSA,这是阻止人们这样做的更好方法。
  • 我已经用 rails 5.1 试过了,但由于某种原因它不起作用。未调用覆盖函数
【解决方案2】:

如果你想在 Rails 3.2 中访问模型本身,你应该使用proxy_association.owner

例子:

class Author < ActiveRecord::Base
  has_many :books do
    def << (book)
      proxy_association.owner.add_book(book)
    end
  end

  def add_book (book)
    # do your thing here.
  end
end

documentation

【讨论】:

  • 在 Rails 5.1 中仍然如此
  • @coconup 这在 rails 5.1 中对我不起作用-根本不调用重写
【解决方案3】:

我认为您想要 def tags.&lt;&lt;(*new_tags) 作为签名,它应该可以工作,或者如果您需要覆盖多个方法,则以下是等效且更简洁的。

class << tags
  def <<(*new_tags)
    # rawr!
  end
end

【讨论】:

  • 我认为这两种方法都行不通。看起来您的建议是尝试扩展 tags 方法返回的值的 Eigenclass。
  • 它在tags 返回的任何东西的特征类中定义一个方法,这可能是一个数组。这具有向数组添加新实例方法的效果,这是我理解的原始问题要问的。 extend 在 ruby​​ 中具有特定含义,而这不是这里发生的事情。
  • 你是对的,这正是它正在做的事情。我想我只是不明白你建议把代码放在哪里。无论如何,我想我的回答几乎相同,只是更多的上下文。
【解决方案4】:

您必须定义 tags 方法以返回具有 &lt;&lt; 方法的对象。

你可以这样做,但我真的不推荐它。与尝试替换 ActiveRecord 使用的东西相比,向模型中添加一个方法来做你想做的事情会好得多。

这实质上是运行默认的tags 方法,将

def tags_with_append
  collection = tags_without_append
  def collection.<< (*arguments)
    ...
  end
  collection
end
# defines the method 'tags' by aliasing 'tags_with_append'
alias_method_chain :tags, :append  

【讨论】:

    【解决方案5】:

    我使用的方法是扩展关联。你可以在这里看到我处理“数量”属性的方式:https://gist.github.com/1399762

    它基本上允许你做

    has_many : tags, :through => : taggings, extend => QuantityAssociation
    

    如果不确切知道您希望通过覆盖方法实现什么,则很难知道您是否可以这样做。

    【讨论】:

      【解决方案6】:

      这可能对您的情况没有帮助,但可能对其他调查此问题的人有用。

      关联回调: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

      文档中的示例:

      class Project
        has_and_belongs_to_many :developers, :after_add => :evaluate_velocity
      
        def evaluate_velocity(developer)
          ...
        end
      end
      

      另见关联扩展:

      class Account < ActiveRecord::Base
        has_many :people do
          def find_or_create_by_name(name)
            first_name, last_name = name.split(" ", 2)
            find_or_create_by_first_name_and_last_name(first_name, last_name)
          end
        end
      end
      
      person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
      person.first_name # => "David"
      person.last_name  # => "Heinemeier Hansson"
      

      【讨论】:

        【解决方案7】:

        Rails guides 文档关于直接覆盖添加的方法。

        OP 覆盖&lt;&lt; 的问题可能是唯一的例外,请遵循the top answer。但它不适用于has_one= 赋值方法或getter 方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-10-14
          • 1970-01-01
          • 2015-07-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多