Ruby on Rails ActiveRecord单表继承具有一个到超类的关联。

huangapple go评论57阅读模式
英文:

Ruby on Rails ActiveRecord Single Table Inheritance has_one to superclass

问题

I have a Base class which contains STI and named procedures, looking like this:

class Base < ActiveRecord
  self.abstract_class = true
  self.table_name = "procedures"
  self.inheritance_column = nil
end

We also have multiple superclasses named for instance ProcedureA and ProcedureB, looking like this:

class ProcedureA < Base
end

class ProcedureB < Base
end

A procedure can contain another procedure so I'd like to add this to the base class. But when retrieving the other procedure I'd like to know the actual class name. So this works:

class Base < ActiveRecord
  self.abstract_class = true
  self.table_name = "procedures"
  self.inheritance_column = nil

  has_one :parent, class_name: "Base"
end

But when retrieving it from the database, it returns it as the Base class but not the specific class that was assigned.

For instance:

procedure_a = ProcedureA.create!()
procedure_b = ProcedureB.create!()
procedure_a.parent = procedure_b
procedure_a.save!

procedure_a.parent => Is ProcedureB
ProcedureA.first.parent => Is Base class

So at this point, I don't know how to get the actual class instead of the base class. I tried playing with polymorphic: true but it doesn't work.

英文:

I have a Base class which contains STI and named procedures, looking like this:

class Base < ActiveRecord
  self.abstract_class = true
  self.table_name = "procedures"
  self.inheritance_column = nil
end

We also have multiple superclasses named for isntance ProcedureA and ProcedureB, looking like this:

class ProcedureA < Base
end

class ProcedureB < Base
end

A procedure can contain another procedure so i'd like to add this to the base class. But when retrieving the other procedure i'd like to know the actual class name. So this works:

class Base < ActiveRecord
  self.abstract_class = true
  self.table_name = "procedures"
  self.inheritance_column = nil

  has_one :parent, class_name: "Base"
end

But when retrieving it form the database it returns is as the Base class but not the specific class that was assigned.

For instance:

procedure_a = ProcedureA.create!()
procedure_b = ProcedureB.create!()
procedure_a.parent = procedure_b
procedure_a.save!

procedure_a.parent => Is ProcedureB
ProcedureA.first.parent => Is Base class

So at this point i don't know how to get the actual class instead of the base class. I tried playing with polymorphic: true but doesn't work.

答案1

得分: 2

创建一个self referential association时,需要使用belongs_to而不是has_one

class User
  belongs_to :manager, 
    class_name: 'User',
    optional: true
  has_many :subordinates,
    class_name: 'User',
    foreign_key: :manager_id
end

毕竟外键是存储在这个模型的表上

但还有一个问题,即您的Base正在积极地破坏单表继承。它应该是这样的:

class Base < ActiveRecord
  self.table_name = "procedures" # 所有子类都将存储在这个表中
end

self.abstract_class = true 告诉ActiveRecord在解析其子类的表名称时忽略此类。这与您的需求相反。

self.inheritance_column = nil 禁用了您需要让单表继承工作的类型推断。

在Rails中,STI的工作方式实际上无需执行任何操作。任何继承自ActiveRecord::Base且不是抽象的类默认都是STI的父类。您只需添加一个type列,然后子类就能使用了。

添加带有STI的自引用关联与上面的示例几乎相同,但有一个小变化:

class Base < ActiveRecord
  belongs_to :parent,
    class_name: 'Base',
    optional: true
end

class_name: 'Base', 看起来有点奇怪,但实际上只是告诉Rails在哪个表上查找关联的记录。在加载记录时,类型推断将覆盖实例化的类(或者至少在不破坏它的情况下会这样)。

请注意,这不是您需要多态关联的情况。多态关联用于指向多个不同表的单个关联。

英文:

There is a lot of confusion going on here.

To create a self referential assocation you need to use belongs_to not has_one.

class User
  belongs_to :manager, 
    class_name: &#39;User&#39;,
    optional: true
  has_many :subordinates,
    class_name: &#39;User&#39;,
    foreign_key: :manager_id
end

After all the foreign key is stored on this models table.

But then there is also the fact that your Base is actively sabotaging Single Table Inheritance. It should just read:

class Base &lt; ActiveRecord
  self.table_name = &quot;procedures&quot; # all children will be stored in this table
end

self.abstract_class = true tells ActiveRecord to ignore this class when resolving the table name of its children. That's the opposite of what you want.

self.inheritance_column = nil disables the type inferance that you need for single table inheritance to work.

The way STI in Rails works you don't actually have to do anything. Any class that inherits from ActiveRecord::Base and is not abstract is a STI parent class by default. All you need to do is add a type column and subclasses and bob's your uncle.

Adding the self-referential assocation with STI is pretty much the same as above example but with a twist:

class Base &lt; ActiveRecord
  belongs_to :parent,
    class_name: &#39;Base&#39;,
    optional: true
end

class_name: &#39;Base&#39;, seems kind of odd but its really just telling Rails which table to look for the assocatiated records on. When loading the record the type inferance will override what class is instanciated anyways (or at least it would if you didn't sabotage it).

Note that this is not a case where you want a polymorphic assocation. Thats used when you have a single assocation that points to multiple different tables.

huangapple
  • 本文由 发表于 2023年2月8日 23:52:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/75388357.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定