我的 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
类中一次指定所有关联的外键?
答案 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
。
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_id
和Nested
类中定义或重新定义的所有关联中查找MyModel
外键。