重新定义 Rails 中所有关联的外键名称
Redefine foreign key name for all associations in Rails
我有 一个 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 名称)。
我可以在里面输入 class Nested
:
has_many :things, foreign_key: 'my_model_id'
has_many :other_things, foreign_key: 'my_model_id'
但是如果可能的话,如何一次指定所有关联的外键 in Nested
class?
如果您的所有 has_many
协会都在 MyModel
上声明,您应该没问题,或者您可能会受益于升级 Rails;以下内容在 4.2.4 中非常适合我。 Rails 使用声明 has_many
的 class 来生成 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
,它使用在您的 has_many
上声明的 foreign_key
(或 as
)选项或从 class 名称派生。没有明显的方法来自定义这些方法,除非你做一些不可取的事情,比如 override Nested.name
.
def foreign_key
@foreign_key ||= options[:foreign_key] || derive_foreign_key
end
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
我的解决方案可能不被推荐,但它有效。这是:
class Nested < MyModel
def self.name
MyModel.name
end
end
ActiveRecord
将为 Nested
和 MyModel
类.[=15= 中定义或重新定义的所有关联寻找 my_model_id
外键]
我有 一个 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 名称)。
我可以在里面输入 class Nested
:
has_many :things, foreign_key: 'my_model_id'
has_many :other_things, foreign_key: 'my_model_id'
但是如果可能的话,如何一次指定所有关联的外键 in Nested
class?
如果您的所有 has_many
协会都在 MyModel
上声明,您应该没问题,或者您可能会受益于升级 Rails;以下内容在 4.2.4 中非常适合我。 Rails 使用声明 has_many
的 class 来生成 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
,它使用在您的 has_many
上声明的 foreign_key
(或 as
)选项或从 class 名称派生。没有明显的方法来自定义这些方法,除非你做一些不可取的事情,比如 override Nested.name
.
def foreign_key
@foreign_key ||= options[:foreign_key] || derive_foreign_key
end
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
我的解决方案可能不被推荐,但它有效。这是:
class Nested < MyModel
def self.name
MyModel.name
end
end
ActiveRecord
将为 Nested
和 MyModel
类.[=15= 中定义或重新定义的所有关联寻找 my_model_id
外键]