首先,继承方法可能不正确。如果是这样,请解释一下不同的方法。
以下是设置示例。
我有许多应用程序,除了使用完全相同的数据库,没有连接。为了干掉代码库,我有一个包含所有活动记录类的引擎。我想在主要应用程序中保留特定于应用程序的范围和方法。
在我的Rails引擎中,
class MyEngine::User < ActiveRecord::Base
has_many :dogs
end
class MyEngine::Dog < ActiveRecord::Base
belongs_to :user
end
在我的主应用程序中,
class User < MyEngine::User
end
class Dog < MyEngine::Dog
# EDITED TO SHOW EXAMPLE OF HOW SCOPE DOESN'T BELONG IN ENGINE
DOGS_WITH_SPOTS_IDS = [ 1, 2, 3, 4 ]
scope :with_spots, -> { where(id: DOGS_WITH_SPOTS_IDS) }
end
我的主要应用中的问题,
user = User.last
user.dogs
# => #<ActiveRecord::Associations::CollectionProxy[#<MyEngine::Dog spots: true>]>
user.dogs.with_spots
NoMethodError: undefined method 'with_spots' for #<MyEngine::Dog::ActiveRecord_Association_CollectionProxy:0x007fa4bf838e50>
虽然这有效但
class User < MyEngine::User
has_many :dogs
end
我不想重新定义/定义主要应用程序中的所有关联。
这种类型的问题只会在我的所有主要应用程序中以不同的形式出现。这让我觉得可能有办法重新定义这些协会。
有没有办法评估子类而不是超类的关联,就像STI模式一样,让Rails帮助器识别不同的子类?
意思是,我希望我的主应用程序User#dogs返回主应用程序Dog而不是MyEngine :: Dog。
# may not be the exact code, just off the top of my head
instance_eval do
def model_name
self.class.name
end
end
答案 0 :(得分:1)
重要提示即使没有继承,也无法查询user.dogs.with_spots
。你可能想要在Dog
中获得user.dogs.to_a
数组。
解决方案解决问题的最常用方法是使用Single Table Inheritance。
让我们看看它是如何运作的。
我将假设已经为所有必需列定义了表my_engine_users
和my_engine_dogs
(与MyEngine
模块一样多。)
类MyEngine::User
和MyEngine::Dog
保持不变。
类User
和Dog
只是扩展了各自的引擎类,而没有关于它们之间关系的任何其他声明。
我们所做的,我们告诉Rails,这两个表可能被不同的类使用(通过扩展)。通过向表中添加type
列可以轻松实现。
add_column :my_engine_users, :type, :string
和
add_column :my_engine_dogs, :type, :string
现在,您可以与您的用户和狗一起做所有精彩的事情。
dimakura = User.create(username: 'dimakura')
dora = Dog.create(name: 'Dora', owner: dimakura)
fanta = Dog.create(name: 'Fanta', owner: dimakura)
dimakura.dogs.to_a # => array of Dogs, not MyEngine::Dogs
通过在type
列中明确编写类名来实现这种魔力。
p dimakura #=> #<User id: 1, username: "dimakura", type: "User">
p dora #=> #<Dog id: 1, owner_id: 1, name: "Dora", type: "Dog">
p fanta #=> #<Dog id: 2, owner_id: 1, name: "Fanta", type: "Dog">
答案 1 :(得分:0)
MyEngine::User.dogs
返回[由关联实例代理]一组MyEngine::Dog
个实例,通常可能没有with_spots
范围。
如果您知道所有MyEngine::Dog
s“回复”with_spots
,只需将范围定义移至MyEngine::Dog
即可。如果不是这种情况,则在MyEngine::User.dogs
上调用此范围是没有意义的。
UPD 添加的示例可能已解决:
class MyEngine::Dog < ActiveRecord::Base
belongs_to :user
scope :with_spots, -> { self.class.const_get(:SCOPE_PROC) }
end
class Dog < MyEngine::Dog
DOGS_WITH_SPOTS_IDS = [ 1, 2, 3, 4 ]
SCOPE_PROC = -> { where(name: DOGS_WITH_SPOTS_IDS }
end
答案 2 :(得分:0)
我只是想发布一个跟进:
虽然@dimakura是正确的,但它仍然无法正常工作。这是因为Rails不允许为STI链接范围。最后,我删除了STI并使用了以下内容:
user = User.first
user.dogs.merge(Dog.with_spots)