如何使用 Ruby ActiveRecord 建模这种多重继承关系?

发布于 2024-07-24 07:12:02 字数 307 浏览 7 评论 0原文

假设我有 5 张桌子。 ActiveRecord 可以处理这个问题吗? 你会如何设置它?

层次结构:

Account (Abstract)
  CorporateCustomer (Abstract)
    PrivateCustomer
    PublicCustomer
  GovernmentCustomer

编辑: 在 nhibernate 和 castle activerecord 中,启用此场景所需的方法称为“joined-subclasses”。

Assuming I have 5 tables. Can ActiveRecord handle this? How would you set it up?

The hierarchy:

Account (Abstract)
  CorporateCustomer (Abstract)
    PrivateCustomer
    PublicCustomer
  GovernmentCustomer

Edit: In nhibernate and castle activerecord the method needed to enable this scenario is called "joined-subclasses".

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

余生再见 2024-07-31 07:12:02

您可以尝试按照以下方式进行操作。

class Account < ActiveRecord::Base
  belongs_to :corp_or_gov_customer, :polymorphic => true

  def account_id
    self.id
  end
end

class GovernmentCustomer < ActiveRecord::Base
  has_one :account, :as => :corp_or_gov_customer, :dependent => :destroy

  def method_missing( symbol, *args )
    self.account.send( symbol, *args )
  end
end

class CorporateCustomer < ActiveRecord::Base
  has_one :account, :as => :corp_or_gov_customer, :dependent => :destroy
  belongs_to :priv_or_pub_customer, :polymorphic => true

  def method_missing( symbol, *args )
    self.account.send( symbol, *args )
  end
end

class PrivateCustomer < ActiveRecord::Base
  has_one :corporate_customer, :as => :priv_or_pub_customer, :dependent => :destroy

  def method_missing( symbol, *args )
    self.corporate_customer.send( symbol, *args )
  end
end

class PublicCustomer < ActiveRecord::Base
  has_one :corporate_customer, :as => :priv_or_pub_customer, :dependent => :destroy

  def method_missing( symbol, *args )
    self.corporate_customer.send( symbol, *args )
  end
end

我还没有测试过这段代码(甚至没有检查过它的语法)。 相反,它只是为了向您指出多态关系的方向。

重写 method_missing 来调用嵌套对象可以节省编写代码,就像

my_public_customer.corporate_customer.account.some_attribute

您可以只写

my_public_customer.some_attribute

响应评论:

问题是像“是一个”,“有很多”和“属于”这样的概念都是由外键关系实现的关系模型。 继承的概念对于 RDB 系统来说是完全陌生的。 这些关系的语义必须通过您选择的 ORM 技术映射到关系模型上。

但是 Rails 的 ActiveRecord 库没有实现“is_a”作为模型之间的关系。

有多种方法可以在 RDB 中对类层次结构进行建模。

所有帐户的单个表,但具有冗余属性 - ActiveRecord 只需向表中添加“类型”列即可支持此功能。 然后像这样创建类层次结构:

class Account < ActiveRecord::Base
class GovernmentCustomer < Account
class CorporateCustomer < Account
class PublicCustomer < CorporateCustomer
class PrivateCustomer < CorporateCustomer

然后,如果您调用 PrivateCustomer.new,类型字段将自动设置为“PrivateCustomer”,并且当您调用 Account.find 时,返回的对象将属于正确的类。

这是我推荐的方法,因为这是迄今为止做你想做的事情的最简单的方法。

每个具体类一个表 - 据我所知,ActiveRecord 中没有为此提供映射。 此方法的主要问题是,要获取所有帐户的列表,您必须连接三个表。 我们需要的是某种主索引,它会引导出下一个模型。

每个类一个表 - 您可以将表示抽象类的表视为一种统一索引,或者存储在具体类的表中的对象目录。 通过这种方式思考,您正在将 is_a 关系更改为 has_a 关系,例如对象 has_a index_entry 和 index_entry owns_to 该对象。 这可以由 ActiveRecord 使用多态关系来映射。

书中对这个问题有很好的讨论 “使用 Rails 进行敏捷 Web 开发”(从第二版第 341 页开始)

You could try something along the following lines.

class Account < ActiveRecord::Base
  belongs_to :corp_or_gov_customer, :polymorphic => true

  def account_id
    self.id
  end
end

class GovernmentCustomer < ActiveRecord::Base
  has_one :account, :as => :corp_or_gov_customer, :dependent => :destroy

  def method_missing( symbol, *args )
    self.account.send( symbol, *args )
  end
end

class CorporateCustomer < ActiveRecord::Base
  has_one :account, :as => :corp_or_gov_customer, :dependent => :destroy
  belongs_to :priv_or_pub_customer, :polymorphic => true

  def method_missing( symbol, *args )
    self.account.send( symbol, *args )
  end
end

class PrivateCustomer < ActiveRecord::Base
  has_one :corporate_customer, :as => :priv_or_pub_customer, :dependent => :destroy

  def method_missing( symbol, *args )
    self.corporate_customer.send( symbol, *args )
  end
end

class PublicCustomer < ActiveRecord::Base
  has_one :corporate_customer, :as => :priv_or_pub_customer, :dependent => :destroy

  def method_missing( symbol, *args )
    self.corporate_customer.send( symbol, *args )
  end
end

I've not tested this code (or even checked it for syntax). Rather it's intended just to point you in the direction of polymorphic relations.

Overriding method_missing to call nested objects saves writing code like

my_public_customer.corporate_customer.account.some_attribute

instead you can just write

my_public_customer.some_attribute

In response to the comment:

The problem is that concepts like "is a", "has many" and "belongs to" are all implemented by foreign key relationships in the relational model. The concept of inheritance is completely alien to RDB systems. The semantics of those relationships has to be mapped onto the relational model by your chosen ORM technology.

But Rails' ActiveRecord library doesn't implement "is_a" as a relationship between models.

There are several ways to model your class hierarchy in an RDB.

A single table for all accounts but with redundant attributes - this is supported by ActiveRecord simply by adding a "type" column to your table. and then creating your class hierarchy like this:

class Account < ActiveRecord::Base
class GovernmentCustomer < Account
class CorporateCustomer < Account
class PublicCustomer < CorporateCustomer
class PrivateCustomer < CorporateCustomer

Then if you call PrivateCustomer.new the type field will automatically be set to "PrivateCustomer" and when you call Account.find the returned objects will be of the correct class.

This is the approach I would recommend because it's by far the simplest way to do what you want.

One table for each concrete class - As far as I know there is no mapping provided for this in ActiveRecord. The main problem with this method is that to get a list of all accounts you have to join three tables. What is needed is some kind of master index, which leads to the next model.

One table for each class - You can think of tables that represent the abstract classes as a kind of uniform index, or catalogue of objects that are stored in the tables for the concrete classes. By thinking about it this way you are changing the is_a relationship to a has_a relationship e.g. the object has_a index_entry and the index_entry belongs_to the object. This can be mapped by ActiveRecord using polymorphic relationships.

There is a very good discussion of this problem in the book "Agile Web Development with Rails" (starting on page 341 in the 2nd edition)

一抹淡然 2024-07-31 07:12:02

搜索 ActiveRecord 单表继承功能。
不幸的是,我无法在网上找到更详细的参考链接。 我读到的最详细的解释来自《The Rails Way》一书。

Search for ActiveRecord Single Table Inheritance feature.
Unfortunately I can't find a more detailed reference online to link to. The most detailed explanation I read was from "The Rails Way" book.

青衫负雪 2024-07-31 07:12:02

这是一种方法(最简单的):

class Account < ActiveRecord::Base
   self.abstract_class = true
   has_many :corporate_customers
   has_many :government_customers
end


class CorporateCustomer < ActiveRecord::Base
   self.abstract_class = true
   belongs_to :account
   has_many :private_customers
   has_many :public_customers
end


class PrivateCustomer < ActiveRecord::Base
   belongs_to :corporate_customer
end

class PublicCustomer < ActiveRecord::Base
   belongs_to :corporate_customer
end

class GovernmentCustomer < ActiveRecord::Base
   belongs_to :account
end

注意:抽象模型是不能有对象(不能实例化)的模型,因此它们也没有关联的表。 如果你想要表,那么我无法理解为什么它需要一个抽象类。

This is one way (the simplest) of doing it:

class Account < ActiveRecord::Base
   self.abstract_class = true
   has_many :corporate_customers
   has_many :government_customers
end


class CorporateCustomer < ActiveRecord::Base
   self.abstract_class = true
   belongs_to :account
   has_many :private_customers
   has_many :public_customers
end


class PrivateCustomer < ActiveRecord::Base
   belongs_to :corporate_customer
end

class PublicCustomer < ActiveRecord::Base
   belongs_to :corporate_customer
end

class GovernmentCustomer < ActiveRecord::Base
   belongs_to :account
end

NOTE: Abstract models are the models which cannot have objects ( cannot be instantiated ) and hence they don’t have associated table as well. If you want to have tables, then I fail to understand why it needs to an abstract class.

暖风昔人 2024-07-31 07:12:02

假设大部分数据是共享的,您只需要一张表:accounts。 假设帐户有一个字符串类型列,这将起作用。

class Account < ActiveRecord::Base
  self.abstract_class = true
end

class CorporateCustomer < Account
  self.abstract_class = true
  has_many financial_statements
end

class PrivateCustomer < CorporateCustomer
end

class PublicCustomer < CorporateCustomer
end

class GovernmentCustomer < Account
end

Google for Rails STI,特别是 Rails STI 摘要,以获得一些更有用的信息。

Assuming most of the data is shared, you only need one table: accounts. This will just work, assuming accounts has a string type column.

class Account < ActiveRecord::Base
  self.abstract_class = true
end

class CorporateCustomer < Account
  self.abstract_class = true
  has_many financial_statements
end

class PrivateCustomer < CorporateCustomer
end

class PublicCustomer < CorporateCustomer
end

class GovernmentCustomer < Account
end

Google for Rails STI, and in particular Rails STI abstract, to get some more useful info.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文