重新定义Rails

时间:2015-12-16 14:27:23

标签: ruby-on-rails ruby activerecord foreign-keys sti

我的 STI 模型有很多关联:

class MyModel < ActiveRecord::base
  has_many :things
  has_many :other_things
  # ... a lot of `has_many`
end

然后我将非STI 模型添加为嵌套只是为了向MyModel添加一些特定行为而不直接扩展它:

class Nested < MyModel
  self.inheritance_column = nil
end

但是我的协会不起作用。他们有my_model_id列,因为它们引用了MyModel,他们也应该引用Nested。但是所有这些has_many都希望使用nested_id列作为外键(它取决于类名)。

我可以在class Nested内输入:

has_many :things, foreign_key: 'my_model_id'
has_many :other_things, foreign_key: 'my_model_id'

但如果可能,如何在Nested类中一次指定所有关联的外键

2 个答案:

答案 0 :(得分:1)

如果在has_many上声明了所有MyModel关联,那么您应该没问题,或者您可以从升级Rails中受益;以下在4.2.4中完美适用于我。 Rails使用声明has_many的类来生成foreign_key,因此即使Nested继承,:things仍会被my_model_id查找。

class MyModel < ActiveRecord::Base
  has_many :things
end

class MyA < MyModel
end

class Nested < MyModel
  self.inheritance_column = nil
end

class Thing < ActiveRecord::Base
  belongs_to :my_model
end
> m = MyModel.create
 => #<MyModel id: 1, type: nil, created_at: "2015-12-22 02:21:16", updated_at: "2015-12-22 02:21:16">
> m.things.create
 => #<ActiveRecord::Associations::CollectionProxy [#<Thing id: 1, my_model_id: 1, created_at: "2015-12-22 02:21:19", updated_at: "2015-12-22 02:21:19">]>
> n = Nested.create
 => #<Nested id: 2, type: nil, created_at: "2015-12-22 02:21:27", updated_at: "2015-12-22 02:21:27">
> n.things.create
 => #<ActiveRecord::Associations::CollectionProxy [#<Thing id: 2, my_model_id: 2, created_at: "2015-12-22 02:21:32", updated_at: "2015-12-22 02:21:32">]>
> n.reload.things
  Nested Load (0.2ms)  SELECT  "my_models".* FROM "my_models" WHERE "my_models"."id" = ? LIMIT 1  [["id", 2]]
  Thing Load (0.1ms)  SELECT "things".* FROM "things" WHERE "things"."my_model_id" = ?  [["my_model_id", 2]]
 => #<ActiveRecord::Associations::CollectionProxy [#<Thing id: 2, my_model_id: 2, created_at: "2015-12-22 02:21:32", updated_at: "2015-12-22 02:21:32">]>

如果您正在为Nested提供自己的has_many关联,则生成外键的代码非常深且无法访问。您最终使用HasManyReflection,该foreign_key使用foreign_key上声明的as(或has_many)选项,或者从类名派生。没有明显的方法来自定义这些方法,除非你做一些不可取的事情,比如覆盖Nested.name

derive_foreign_key

def foreign_key
  @foreign_key ||= options[:foreign_key] || derive_foreign_key
end

{{3}}:

def derive_foreign_key
  if belongs_to?
    "#{name}_id"
  elsif options[:as]
    "#{options[:as]}_id"
  else
    active_record.name.foreign_key
  end
end

所以最简单的方法可能就是循环:

class Nested < MyModel
  self.inheritance_column = nil

  %i[things other_things].each do |association|
    has_many association, foreign_key: 'my_model_id'
  end
end

或者,如果您感觉元编程,请重新定义has_many

class Nested < MyModel
  self.inheritance_column = nil

  def self.has_many(klass, options={})
    options.reverse_merge!(foreign_key: 'my_model_id')
    super
  end

  has_many :things
  has_many :other_things
end

答案 1 :(得分:-1)

我的解决方案可能不推荐,但可行。这是:

class Nested < MyModel
  def self.name
    MyModel.name
  end
end

ActiveRecord将在my_model_idNested类中定义或重新定义的所有关联中查找MyModel外键。