belongs_to belongs_to association only only has_many或has_one

你能在Rails中拥有belongs_to belongs_to关系吗?

搜索 结果给了我两个结果。 事实上我找不到关于这个主题的非常多的信息,这似乎表明它不应该被做或者是不好的做法。

我昨天问了一个关于a_many关系的问题,但是因为我找不到相关的信息,我会生成一个问题,以便人们在将来更容易搜索这个问题。 我正在解释另一个用户的答案(我希望这没关系)。

鞋子可以有很多袜子,但只有一个活跃的袜子。 其他袜子不活跃。 所有袜子都是独一无二的独特图案。 我想确保我没有相同图案的袜子。 我想我可以这三种方式做到这一点

 class Sock < ActiveRecord::Base belongs_to :shoe end 

要确定袜子是活动还是非活动,请给它的’所有者鞋提供对其活动袜子的引用,如下所示:

 class Shoe < ActiveRecord::Base belongs_to :sock end 

去它的主人Shoe并检查Shoe的活动袜子是否是当前的袜子。 例如

 def is_active owner_shoe.active_sock == self 

将它们与外键相关联

 class CreateGettingDressed < ActiveRecord::Migration def change create_table :shoes do |t| t.belongs_to :active_sock, foreign_key: "sock_id" t.string :size t.timestamps null: false end create_table :socks do |t| t.belongs :owner_shoe, foreign_key: "shoe_id" t.string :pattern end end end 

你遇到的问题是你的两个function是冲突的:

Shoe可以有很多Socks ,但只有一个活跃的Sock

您希望将两个模型关联到两个不同的关联。 虽然这样做很简单,但我觉得你尝试做的方式有点受限制。

这是我如何设置基本关联:

 #app/models/sock.rb class Sock < ActiveRecord::Base #columns id | shoe_id | name | active (boolean) | created_at | updated_at belongs_to :shoe end #app/models/shoe.rb class Shoe < ActiveRecord::Base #columns id | name | etc | created_at | updated_at has_many :socks scope :active, -> { where(active: true).first } end 

这将使您能够调用:

 @shoe = Shoe.find 1 @shoe.socks.active #-> first sock with "active" boolean as true 

它还会否定包含active?的需要active? sock模型中的方法。 你可以打电话给@shoe.socks.find(2).active? 得到关于它是否有效的回应。


现在,这应该适用于基本function。

但是,你提到了几个扩展:

如果袜子activeinactive

我想确保我没有相同图案的袜子

这增加了我用join模型解决的额外规范( has_many :through ):

 #app/models/sock.rb class Sock < ActiveRecord::Base has_many :shoe_socks has_many :shoes, through: :shoe_socks end #app/models/shoe_sock.rb class ShoeSock < ActiveRecord::Base # columns id | shoe_id | sock_id | pattern_id | active | created_at | updated_at belongs_to :shoe belongs_to :sock belongs_to :pattern end #app/models/shoe.rb class Shoe < ActiveRecord::Base has_many :shoe_socks has_many :socks, through: :shoe_socks, extend: ActiveSock scope :active, -> { where(active: true).first } end 

您可以在此处阅读以下代码的更多信息:

 #app/models/concerns/active_sock.rb module ActiveSock #Load def load captions.each do |caption| proxy_association.target << active end end #Private private #Captions def captions return_array = [] through_collection.each_with_index do |through,i| associate = through.send(reflection_name) associate.assign_attributes({active: items[i]}) return_array.concat Array.new(1).fill( associate ) end return_array end ####################### # Variables # ####################### #Association def reflection_name proxy_association.source_reflection.name end #Foreign Key def through_source_key proxy_association.reflection.source_reflection.foreign_key end #Primary Key def through_primary_key proxy_association.reflection.through_reflection.active_record_primary_key end #Through Name def through_name proxy_association.reflection.through_reflection.name end #Through def through_collection proxy_association.owner.send through_name end #Captions def items through_collection.map(&:active) end #Target def target_collection #load_target proxy_association.target end 

此设置基本上将所有“逻辑”放入连接模型中。 IE你将拥有一个袜子数据库,一个鞋子和一个连接数据库,两者都有。

这仍然允许您调用@shoe.socks.active但不必降低数据模型中的数据完整性。

我还添加了一些我后来编写的代码 - 这使您能够从连接模型中访问属性。 它使用ActiveRecord中的proxy_association对象,因此它不再调用SQL。

这个添加的代码会追加active? 属性为任何关联的Sock对象。

在Rails中, belongs_to是模型具有外键的指示符。 例如,如果袜子belongs_to鞋子,那么你的袜子桌子将有一个shoe_id字段。 有许多协会可供你使用,但同时使用鞋子和袜子belongs_to不必要的复杂声音。

最直接的解决方案是稍微改变语言,并认为每个袜子都有一个活跃的鞋子。 这样,如果你将袜子换到另一只鞋子,那么你就没有任何东西来整理鞋子了。 您的数据更有可能保持一致。

 class Shoe < ActiveRecord::Base has_one :sock end class Sock < ActiveRecord::Base belongs_to :shoe end 

你现在可以叫shoe.sock来买一双鞋的活动袜子,或者sock.shoe来买袜子的鞋子。


或者,也许你希望每双鞋都有一个专用的袜子池。 我们可以扩展我们以前的解决方案以支持这一点,将我们的关系命名为active_sock以使事情变得更清晰,并使用validation器来确保我们的袜子来自池:

 class Shoe < ActiveRecord::Base has_many :socks has_one :active_sock, class_name: "Sock", foreign_key: "active_shoe_id" validates :active_sock_id, inclusion: { in: sock_ids } end class Sock < ActiveRecord::Base belongs_to :shoe def active? shoe.active_sock == self end end 

现在你可以给shoe.socks打电话来获得所有可用的袜子,鞋子和shoe.active_sock来获得活跃的袜子。 从袜子那边,然后sock.shoe返回哪个鞋“拥有”这个袜子,并袜子sock.active? 返回此袜子当前是否有效。