【问题标题】:belongs_to belongs_to association only no has_many or has_onebelongs_to belongs_to 关联只有 no has_many 或 has_one
【发布时间】:2015-09-25 02:40:10
【问题描述】:

你能在 Rails 中建立belongs_tobelongs_to 关系吗?

Searchresults 给了我两个结果。我找不到关于该主题的太多信息这一事实似乎表明不应该这样做或者是不好的做法。

我向yesterday 询问了has_many 关系,但我想因为找不到这方面的信息,所以我会生成一个问题,以便人们将来更容易搜索。我在解释另一个用户的答案(我希望这没问题)。

一个 Shoe 可以有多个 Socks,但只有一个活动 Sock。其他袜子处于非活动状态。所有袜子也都是独一无二的,具有独特的图案。我想确保我没有相同图案的袜子。我想我可以通过三种方式做到这一点

class Sock < ActiveRecord::Base
  belongs_to :shoe
end

要确定 Sock 是处于活动状态还是处于非活动状态,请为其所有者的鞋子提供对其处于活动状态的 Sock 的引用,如下所示:

class Shoe < ActiveRecord::Base
  belongs_to :sock
end

转到其所有者 Shoe 并检查 Shoe 的活动袜子是否是当前 Sock。例如

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

【问题讨论】:

    标签: ruby-on-rails associations model-associations belongs-to


    【解决方案1】:

    在 Rails 中,belongs_to 表示模型具有外键。例如,如果一只袜子belongs_to 一只鞋,那么你的袜子表将有一个shoe_id 字段。 a number of associations 可供您使用,但鞋子和袜子都使用 belongs_to 听起来不必要的复杂。

    最直接的解决方案是稍微改变语言,将每只袜子想象成一只活动鞋。这样,如果您将袜子换成另一只鞋,那么您就没有任何东西可以整理在鞋子上了。这样,您的数据更有可能保持一致。

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

    您现在可以致电 shoe.sock 获取鞋子的活动袜子,或致电 sock.shoe 获取袜子的鞋子。


    或者,也许您希望每只鞋都有一个专用的袜子池。我们可以扩展我们之前的解决方案来支持它,将我们的关系命名为 active_sock 以使事情更清楚,并使用验证器来确保我们的 sock 来自池:

    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? 返回这只袜子当前是否处于活动状态。

    【讨论】:

      【解决方案2】:

      您遇到的问题是您的两个功能相互冲突:

      一个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
      

      它还将消除在您的sock 模型中包含active? 方法的需要。您可以致电@shoe.socks.find(2).active? 以获取有关它是否处于活动状态的回复。


      现在,这个应该可以很好地用于基本功能。

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

      如果 Sock 是 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
      

      您可以阅读以下代码here的更多信息:

      #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 你将有一个 socksshoes 之一的数据库和一个连接数据库,其中包含两者的配对。

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

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

      此添加的代码会将active? 属性附加到任何关联的Sock 对象。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-13
        • 1970-01-01
        • 2023-02-02
        • 2013-10-09
        相关资源
        最近更新 更多