I want to create a base model Person
with some person related attributes like name, address, phone and so on. One Person
can be one ore more of the following:
我想创建一个基本模型Person,其中包含一些与人物相关的属性,如姓名,地址,电话等。一个人可以是以下一种或多种:
-
LoginUser
with fields for login, password, last_login, ... - LoginUser包含登录,密码,last_login,...的字段
-
CardHolder
with fields for card_id, last_entrance, ... - CardHolder,包含card_id,last_entrance,...的字段
-
Supplier
with just a flag whether or not the person is a supplier - 只有一个标志的供应商,无论该人是否是供应商
-
Recipient
with just a flag whether or not the person is a recipient - 无论该人是否是收件人,只收到一个标志的收件人
Is there a common sense or best practise design pattern in Ruby on Rails to represent that inheritance? How it should be represented in the model(s) and table structure so that it is possible to check whether a Person
is a LoginUser
and to access the corresponding fields. In another project I worked already with STI but in this case this isn't the right pattern.
Ruby on Rails中是否存在常识或最佳实践设计模式来表示继承?它应该如何在模型和表结构中表示,以便可以检查Person是否是LoginUser并访问相应的字段。在另一个项目中,我已经与STI合作,但在这种情况下,这不是正确的模式。
2 个解决方案
#1
1
What you're looking for is a reverse polymorphic association. Polymorphic associations allow you to link one model to many different ones. A reverse polymorphic association allows you to link many models to one single one. They're a little tricky to set up, but once you get the hang of it it's no problem.
您正在寻找的是反向多态关联。多态关联允许您将一个模型链接到许多不同的模型。反向多态关联允许您将多个模型链接到一个模型。它们设置起来有点棘手,但是一旦掌握了它就没问题了。
In order to accomplish this, you need another model that acts as a go-between for the Person
model and each of the different roles. This go-between model is the one that actually has the polymorphic association. Your Person model will has_many
that model, and your various role models will has_one
of it. You then use :through
to make the rest of the necessary associations so your code doesn't know any different. Shazam!
为了实现这一点,您需要另一个模型作为Person模型和每个不同角色的中间人。这种中间模型实际上具有多态关联。您的Person模型将具有该模型,并且您的各种角色模型将具有该模型。然后使用:through来完成其余的必要关联,这样你的代码就不会有任何不同了。快变!
Here's an example of how to do it with the Person
and CardHolder
models. I'm calling the extra model Role
because that seems like an obvious choice:
以下是如何使用Person和CardHolder模型执行此操作的示例。我正在调用额外的模型角色,因为这似乎是一个明显的选择:
class Person < ApplicationRecord
has_many :roles
# Reach through the Roles association to get the CardHolders, via polymorphic :rollable.
# Unfortunately, you can't has_one, so you'll have to enforce uniqueness in Role
# with a validation.
has_many :card_holders, through: :roles, source: :rollable, source_type: 'CardHolder'
end
class Role < ApplicationRecord
belongs_to :person
# Here is where our actual polymorphic connection is:
belongs_to :rollable, polymorphic: true
end
class CardHolder < ApplicationRecord
# The other side of the polymorphic connection, with has_one:
has_one :role, as: :rollable
# Get the person via the role, just like the inverse:
has_one :person, through: :role
end
The database setup is like this:
数据库设置如下:
class CreatePeople < ActiveRecord::Migration[5.1]
def change
create_table :people do |t|
t.string :name
# put in whatever other Person columns you need
t.timestamps
end
end
end
class CreateRoles < ActiveRecord::Migration[5.1]
def change
create_table :roles do |t|
t.references :person, index: true
t.references :rollable, polymorphic: true, index: true
t.timestamps
end
end
end
class CreateCardHolders < ActiveRecord::Migration[5.1]
def change
create_table :card_holders do |t|
t.integer :card_id
t.datetime :last_entrance
# put in whatever other columns you need
t.timestamps
end
end
end
Using it is quite simple:
使用它很简单:
> p = Person.create(name: "Sven Reuter")
# directly add a card holder
> p.card_holders << CardHolder.create(card_id: 1, last_entrance: Time.current)
# build a role instead
> p.roles.build(rollable: CardHolder.new(card_id: 2, last_entrance: Time.current)
# get all of the roles
> p.roles
#2
0
I would go with Person
table and the PersonAttributes
table that is a union of all the attributes the person might have. PersonAttributes
might use STI if applicable, e.g. with LoginUser
storing logins and CardHolder
referencing Card
s.
我会使用Person表和PersonAttributes表,该表是该人可能具有的所有属性的并集。 PersonAttributes可以使用STI(如果适用),例如使用LoginUser存储登录和CardHolder引用卡。
Clean and simple.
干净简单。
#1
1
What you're looking for is a reverse polymorphic association. Polymorphic associations allow you to link one model to many different ones. A reverse polymorphic association allows you to link many models to one single one. They're a little tricky to set up, but once you get the hang of it it's no problem.
您正在寻找的是反向多态关联。多态关联允许您将一个模型链接到许多不同的模型。反向多态关联允许您将多个模型链接到一个模型。它们设置起来有点棘手,但是一旦掌握了它就没问题了。
In order to accomplish this, you need another model that acts as a go-between for the Person
model and each of the different roles. This go-between model is the one that actually has the polymorphic association. Your Person model will has_many
that model, and your various role models will has_one
of it. You then use :through
to make the rest of the necessary associations so your code doesn't know any different. Shazam!
为了实现这一点,您需要另一个模型作为Person模型和每个不同角色的中间人。这种中间模型实际上具有多态关联。您的Person模型将具有该模型,并且您的各种角色模型将具有该模型。然后使用:through来完成其余的必要关联,这样你的代码就不会有任何不同了。快变!
Here's an example of how to do it with the Person
and CardHolder
models. I'm calling the extra model Role
because that seems like an obvious choice:
以下是如何使用Person和CardHolder模型执行此操作的示例。我正在调用额外的模型角色,因为这似乎是一个明显的选择:
class Person < ApplicationRecord
has_many :roles
# Reach through the Roles association to get the CardHolders, via polymorphic :rollable.
# Unfortunately, you can't has_one, so you'll have to enforce uniqueness in Role
# with a validation.
has_many :card_holders, through: :roles, source: :rollable, source_type: 'CardHolder'
end
class Role < ApplicationRecord
belongs_to :person
# Here is where our actual polymorphic connection is:
belongs_to :rollable, polymorphic: true
end
class CardHolder < ApplicationRecord
# The other side of the polymorphic connection, with has_one:
has_one :role, as: :rollable
# Get the person via the role, just like the inverse:
has_one :person, through: :role
end
The database setup is like this:
数据库设置如下:
class CreatePeople < ActiveRecord::Migration[5.1]
def change
create_table :people do |t|
t.string :name
# put in whatever other Person columns you need
t.timestamps
end
end
end
class CreateRoles < ActiveRecord::Migration[5.1]
def change
create_table :roles do |t|
t.references :person, index: true
t.references :rollable, polymorphic: true, index: true
t.timestamps
end
end
end
class CreateCardHolders < ActiveRecord::Migration[5.1]
def change
create_table :card_holders do |t|
t.integer :card_id
t.datetime :last_entrance
# put in whatever other columns you need
t.timestamps
end
end
end
Using it is quite simple:
使用它很简单:
> p = Person.create(name: "Sven Reuter")
# directly add a card holder
> p.card_holders << CardHolder.create(card_id: 1, last_entrance: Time.current)
# build a role instead
> p.roles.build(rollable: CardHolder.new(card_id: 2, last_entrance: Time.current)
# get all of the roles
> p.roles
#2
0
I would go with Person
table and the PersonAttributes
table that is a union of all the attributes the person might have. PersonAttributes
might use STI if applicable, e.g. with LoginUser
storing logins and CardHolder
referencing Card
s.
我会使用Person表和PersonAttributes表,该表是该人可能具有的所有属性的并集。 PersonAttributes可以使用STI(如果适用),例如使用LoginUser存储登录和CardHolder引用卡。
Clean and simple.
干净简单。