Activerecord协会 - 设计问题
概述
我有一组帆俱乐部示例的数据模型,俱乐部有成员
,他们与boat
(例如所有者,船长,船员)。目前以设计ActivereCord协会的最佳方法来回答有关获取数据的一些问题。这将包括以下查询:
boat
有许多成员
,会员
可以属于一个或多个boats
成员
具有与boat
(船长,机组人员,所有者)的第一次设计尝试相关的一个或
角色
多个 >< - 许多到许多 - > 成员
使用has_many_through关联类型结构。加入表将带有与boat
关系类型的属性,这看起来像:
class Boat < ApplicationRecord
has_many :boat_crews
has_many :members, through: :boat_crews
end
class Member < ApplicationRecord
has_many :boat_crews
has_many :boats, through: :boat_crews
end
class BoatCrew < ApplicationRecord
belongs_to :member
belongs_to :boat
end
表boatrews
具有通常的属性id> id> id,inder_id,boat_id ,还包括一个称为
角色的属性:字符串
表示与船的关系。因此,当成员
与船相关联时,将条目放在联接表中以将JOIN信息保存为角色。如果一个成员
既是所有者又说船的船长,则Boatrucks
表中有两个角色。
现在,此启用是预期的activerecord关联查询,例如
b = Boat.find(1)
b.members # show all the members associated with the first Boat
唯一的缺点是,如果成员
具有两个角色(所有者,船长),则B.Members
查询将返回一个包含一个重复的数组(即带有两个角色的成员)。可以使用b.members.uniq
进行删除,这
是一个更好的设计,我以不同的方式建模与船的角色/关系,因此设计尝试2
秒的设计尝试
尝试对此进行一些思考我可以将成员的想法以及与船只和通用船员的概念的关系分开。因此,新的模型尝试是
class Member < ApplicationRecord
has_one :crew
end
class Crew < ApplicationRecord
has_many :boat_crews
has_many :boats, through: :boat_crews
belongs_to :member, optional: true
end
class Boat < ApplicationRecord
has_many :boat_crews
has_many :crews, through: :boat_crews
end
class BoatCrew < ApplicationRecord
belongs_to :boat
belongs_to :crew
end
一个想法是boat
具有CREW
收集,反之亦然,crew
模型识别的类型船员
角色(例如,船员,船长,所有者)。 成员
与机组人员
模型的关联是将成员
信息(例如名称等)进行船
机组人员。
此设计的缺陷是 has-One 之间定义的关系成员
和crew
模型意味着如果您尝试执行此操作,则发生exourne_key故障成员#crew.build 类型创建。
在这一点上,是否有更好的选择
似乎都不正确。第一个设计有效,但需要使用其他约束(需要使用uniq
方法)。第二种设计不起作用,反思可能是正确的,因为我定义的语义是“ has-One”,但我确实有一个以上(例如船的一部分
)。
我想到的一些想法是
- 带有联接表的 boat has_many成员 建模关系的类型
- 是否引入了一个结合表的模型会引入太多的间接级别?
因此,寻求有关如何解决这个问题的任何建议或评论?
Overview
I have a set of data models for a Sailing club example, where there are Members
of the club, who have a relationship with a Boat
(eg Owner, Skipper, Crew). Currently wrestling with the best way to design the ActiveRecord associations to answer some questions on getting data. This would include such queries as:
- A
Boat
has manyMembers
, - A
Member
can belong to one or moreBoats
- A
Member
has one or more roles associated to aBoat
(Skipper, Crew, Owner)
First Design Attempt
So my first design was to have a Boat
<- many to many -> Member
structure using the has_many_through association type. The join table would carry the attribute for the type of relationship with the Boat
This looks like:
class Boat < ApplicationRecord
has_many :boat_crews
has_many :members, through: :boat_crews
end
class Member < ApplicationRecord
has_many :boat_crews
has_many :boats, through: :boat_crews
end
class BoatCrew < ApplicationRecord
belongs_to :member
belongs_to :boat
end
The table BoatCrews
has the usual attributes id, member_id, boat_id
and also includes another attribute called role:string
to represent the relationship to the boat. So when a Member
is associated to a boat and entry is placed in the join table to hold the join information with the role. If a Member
is both and owner and say a skipper of the boat then there are two roles in the BoatCrews
table.
Now what this enables is the expected ActiveRecord Association queries such as
b = Boat.find(1)
b.members # show all the members associated with the first Boat
The only downside is that if a Member
has two roles (Owner, Skipper) then the b.members
query will return an array which contains one duplicate (ie. the Member with two roles). This can be removed with a b.members.uniq
Is there a better design where I model the role/relationship to the Boat differently, hence Design attempt number 2
Second Design Attempt
Thinking on it some more I thought that I could seperate the idea of the member and the relationship to the concept of a boat and a generic crew. So new model attempt is
class Member < ApplicationRecord
has_one :crew
end
class Crew < ApplicationRecord
has_many :boat_crews
has_many :boats, through: :boat_crews
belongs_to :member, optional: true
end
class Boat < ApplicationRecord
has_many :boat_crews
has_many :crews, through: :boat_crews
end
class BoatCrew < ApplicationRecord
belongs_to :boat
belongs_to :crew
end
The idea is that a Boat
has a Crew
collection and vice versa with the Crew
model identifying the type of Crew
role (eg crew, skipper, owner). The association of the Member
to Crew
models is to then take the Member
information (eg name, etc) and associate to the role they have with the Boat
crew model.
The flaw in this design is that the has-one relationship defined between the Member
and Crew
models means that foreign_key faults occur if you try to do a member#crew.build type creation.
Is there a better option
At this point neither approach seems correct. The first design works, but requires use of an additional constraint (needing to use uniq
method). The second design doesn't work and on reflection is probably correct in that the semantics I've defined is 'has-one' but I'm really got more than one (eg Member
has two roles as part of Boat
).
Some ideas I've thought about are
- Revert to the Boat has_many Member with a join table and then use a new model from that join table to model the type of relationship
- Does introducing a model off a join table introduce too many levels of indirection ?
So seeking any suggestions or comments on how to tackle this one ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
除非我误解了某些东西,否则我认为您的设计尝试2无法解决设计尝试的“问题”1。如果成员只能有一个“船员”船队的一部分,现在您有一个重复的船员(指向同一会员)。
尽管这不是一个完美的规则,但是在进行初始体系结构时,将尽可能接近现实世界的事物建模是一个体面的规则。优化可以稍后进行。在这种情况下,我会尝试尝试1,但不要使用
uniq
(因为这需要将所有内容加载到Ruby中,然后获得不同的记录,这很慢)。依靠导轨主动记录查询界面而不是。您可以使用类似的东西,它会生成
选择不同的...
查询您需要唯一的成员。实际上,您很可能想查询了解成员及其角色的知识。我不确定在Pure Rails Active查询接口中执行此操作的最简单方法,但是,您可以始终使用 array_agg 如果您想获得成员及其所有角色。例如,
将返回类似的东西
将为您带来所有成员的名字,以及它们各自的角色作为数组。我将其转换为主动记录查询。
一种相当长的方式说出选项1,并在需要某些查询的帮助时在此处提出具体问题。
Unless I'm misunderstanding something, I don't think your design attempt 2 solves for the 'problem' that arises with design attempt 1. If a member can only have one 'crew', then they would still be doubly counted when being considered a part of a boatcrew, now you have a duplicate crew (that points to the same member).
Though this isn't a perfect rule, when doing initial architecture, it's a decent rule-of-thumb to model things as close to the real world as you can. Optimizations can come later. In this case, I would go with attempt 1, but don't use
uniq
(because that would require loading everything into ruby, and then getting distinct record, which is slow). Rely on rails Active Record Query Interface instead. You can use something like,which generates a
SELECT DISTINCT ...
query for when you need unique members. Realistically though, it's highly likely you want to query for knowledge of the members and their roles. I'm not sure of the easiest way to do this in pure rails active query interface, but, you can always use array_agg if you want to get the member and all of their roles.For example
would return something like
Will get you the names of all members, and all of their respective roles as an array. I leave it to you to convert this to an active record query.
A rather long-winded way of saying go with option 1, and ask specific questions here when you need help with certain queries.