belongs_to belongs_to association only only has_many或has_one

时间:2015-09-25 02:40:11

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

你可以在Rails中建立belongs_to belongs_to关系吗?

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

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

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

class Sock < ActiveRecord::Base
  belongs_to :shoe
end

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

class Shoe < ActiveRecord::Base
  belongs_to :sock
end

去它的主人Shoe并检查Shoe的活动袜子是否是当前的袜子。 E.g。

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

2 个答案:

答案 0 :(得分:4)

在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以使事情更清楚,并使用验证器确保我们的袜子来自池:

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返回哪个鞋子&#34;拥有&#34;这个袜子,sock.active?返回此袜子当前是否有效。

答案 1 :(得分:1)

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

  

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?模型中包含sock方法。您可以致电@shoe.socks.find(2).active?以获得有关其是否有效的回复。

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

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

  

如果袜子是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你将拥有一个 socks 的数据库,一个和一个两者兼容的连接数据库。

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

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

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