Activerecord协会 - 设计问题

发布于 2025-02-03 09:47:01 字数 2566 浏览 2 评论 0原文

概述

我有一组帆俱乐部示例的数据模型,俱乐部有成员,他们与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 many Members,
  • A Member can belong to one or more Boats
  • A Member has one or more roles associated to a Boat (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 技术交流群。

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

发布评论

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

评论(1

清醇 2025-02-10 09:47:01

除非我误解了某些东西,否则我认为您的设计尝试2无法解决设计尝试的“问题”1。如果成员只能有一个“船员”船队的一部分,现在您有一个重复的船员(指向同一会员)。

尽管这不是一个完美的规则,但是在进行初始体系结构时,将尽可能接近现实世界的事物建模是一个体面的规则。优化可以稍后进行。在这种情况下,我会尝试尝试1,但不要使用uniq(因为这需要将所有内容加载到Ruby中,然后获得不同的记录,这很慢)。依靠导轨主动记录查询界面而不是。您可以使用类似的东西,

b.members.distinct

它会生成选择不同的...查询您需要唯一的成员。实际上,您很可能想查询了解成员及其角色的知识。我不确定在Pure Rails Active查询接口中执行此操作的最简单方法,但是,您可以始终使用 array_agg 如果您想获得成员及其所有角色。

例如,

SELECT members.name, array_agg(boat_crews.role) AS roles FROM members
INNER JOIN boat_crews on members.id = boat_crews.member_id
INNER JOIN boats on boat_crews.boat_id = boats.id
WHERE boats.id = 1
GROUP BY members.name

将返回类似的东西


names | roles
joe   | ["rower"]
john  | ["skipper","captain"]

将为您带来所有成员的名字,以及它们各自的角色作为数组。我将其转换为主动记录查询。

一种相当长的方式说出选项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,

b.members.distinct

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

SELECT members.name, array_agg(boat_crews.role) AS roles FROM members
INNER JOIN boat_crews on members.id = boat_crews.member_id
INNER JOIN boats on boat_crews.boat_id = boats.id
WHERE boats.id = 1
GROUP BY members.name

would return something like


names | roles
joe   | ["rower"]
john  | ["skipper","captain"]

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.

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