【问题标题】:STI has_many relationship in parent class inherited by sub classes子类继承的父类中的 STI has_many 关系
【发布时间】:2015-10-08 22:12:28
【问题描述】:

假设我有以下 STI 模型

class Parent < ActiveRecord::Base
  has_many :childs, foreign_key: 'parent_id' # forgive the english for sake of simplicity

  #def childs
  #  Child.where(parent_id: id) # this works just fine BTW
  #end
end

class BadParent < Parent
end

class GoodParent < Parent
end

和下面的子类

class Child
  belongs_to :parent # parent_id lives on this
end

我不关心在 Child 上设置类型,所以我不关心创建多态关联。

bad_parent = BadParent.create(name: 'Michael Jackson')
child = Child.create(name: 'Bobby', parent: bad_parent)

如果我跑了

child.parent #=> <# BadParent > # AWESOME

bad_parent.childs #=> [] NO BUENO!!!

sql_statement = bad_parent.childs.to_sql #=> "SELECT `childs`.* FROM `childs` WHERE `childs`.`parent_id` = 1"

Child.find_by_sql(sql_statement) #=> [<# Child Object #>] BUENO!!!

有什么我必须添加到关联中才能像find_by_sql 这样工作吗?

【问题讨论】:

  • 我不确定我是否理解。你有 #childs 方法和 has_many :childs 关联。你想用哪一个?
  • 对于初学者来说,您应该使用正确的复数形式children 以避免错误。

标签: ruby-on-rails activerecord


【解决方案1】:

与其他 cmets 一样,您不应该同时拥有名称相同的方法和关联,因为目前还不清楚将执行什么 - 我假设在这里您将摆脱 def childs ... 除此之外,我认为您的问题与缓存有关,即 Rails 只有在知道某些内容发生变化时才会访问数据库。在您的示例中, bad_parent 不知道添加了新的孩子。您可以像这样重新加载:

bad_parent.reload
bad_parent.childs #> should show child object now

或强制调用数据库,例如:

bad_parent.childs(true)

查看 rias 指南的 3.1 控制缓存部分了解更多信息:http://guides.rubyonrails.org/association_basics.html

【讨论】:

  • 是的,很奇怪。我知道rails缓存数据库请求,但将其归结为具有STI怪异的东西。 #childs(true) 现在似乎对我有用
【解决方案2】:

这就是我要做的...

#app/models/person.rb
class Person < ActiveRecord::Base
  #columns id | type | parent_id | type_id | name | created_at | updated_at
end

#app/models/parent.rb
class Parent < Person
   belongs_to :type
   has_many :children, foreign_key: 'parent_id'
end

#app/models/child.rb
class Child < Parent
    belongs_to :parent
end

#app/models/type.rb
class Type < ActiveRecord::Base
   #columns id | type
   #values good/bad (probably overkill to have DB for this)
   has_many :parents
end

这应该允许您调用以下内容:

@parent = Parent.find params[:id]
@parent.children #-> collection of Person model with parent_id attribute for parent

关于您的具体问题 - 关于 eager loading 等 - 我没有大量经验。


hierarchy gems 可以帮助解决这个问题。

我们之前使用过ClosureTree - 它创建了一个单独的表,使您可以更轻松地遍历层次结构。另一个叫做ancestry,非常流行(更容易实现)。

我建议在您的“父”模型中使用 ancestry 之类的东西。我将父模型称为Parent,因为我认为它可以让您有更深入的空间来处理不同类型的数据。

例如,您的Child 模型完全是它自己的,它应该由与Parent 相同的数据组成。

【讨论】:

  • 是的,我喜欢你建议的替代方案,但我喜欢当前的实现(已经编码,但可能会为此产生问题)。 +1 建议 ancestryclosureTree 宝石。我以前没有想到这些。似乎我真正的问题是 AR db 缓存与 STI 混淆了。感谢您的帮助!
猜你喜欢
  • 2020-04-06
  • 2023-03-27
  • 2012-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-31
  • 2012-04-11
  • 1970-01-01
相关资源
最近更新 更多