【问题标题】:Rails model associations, has_many :through and has_one with the same modelRails 模型关联,has_many :through 和 has_one 具有相同的模型
【发布时间】:2010-12-01 02:27:53
【问题描述】:

我有两个模型:用户和状态。州模型包含美国 50 个州中每一个州的记录。

我希望每个用户都有两个属性:一个“家”状态和多个“访问”状态。

我知道我必须建立某种模型关联来实现这一点,但不确定最好的方法是什么。

这是我到目前为止所拥有的,但我知道将 has_many 和 has_one 关联到同一个模型肯定有问题。

#user.rb
class User < ActiveRecord::Base
   has_many :visits
   has_many :states, :through => :visits
   has_one :state
end

#visit.rb
class Visit < ActiveRecord::Base
   belongs_to :user
   belongs_to :state
end

#state.rb
class State < ActiveRecord::Base
   has many :visits
   has many :users, :through => :visits 
   belongs_to :user
end
   

有什么建议吗?

【问题讨论】:

    标签: ruby-on-rails activerecord associations


    【解决方案1】:

    在我看来,你已经拥有的几乎是正确的,除了你会像这样将主状态外键存储在用户身上:

    # user.rb
    class User < ActiveRecord::Base
      belongs_to :state
      has_many :visits
      has_many :states, through: visits
    end
    
    # visit.rb
    class Visit < ActiveRecord::Base
      belongs_to :user
      belongs_to :state
    end
    
    # state.rb
    class State < ActiveRecord::Base
      has_many :visits
      has_many :users, through: :visits
    end
    

    然后您将像这样访问主状态:

    u = User.first
    u.state
    

    以及访问过的状态,如下所示:

    u = User.first
    u.states
    

    为了编程清晰,您可以重命名您的关系:

    # user.rb
    class User < ActiveRecord::Base
      belongs_to :home_state, class_name: "State"
      has_many :visits
      has_many :visited_states, class_name: "State", through: visits
    end
    
    # state.rb
    class State < ActiveRecord::Base
      has_many :residents, class_name: "User"
      has_many :visits
      has_many :visitors, class_name: "User", through: :visits
    end
    

    你的领域模型会更有意义:

    u = User.first
    u.home_state
    u.visited_states
    
    s = State.first
    s.residents
    s.visitors
    

    我预计您可能想要存储有关访问的其他信息,因此保留 Visit 模型的 HMT 连接表将允许您执行此操作,而不是使用 HABTM 关系。然后您可以为访问添加属性:

    # xxxxxxxxxxxxxxxx_create_visits.rb
    class CreateVisits < ActiveRecord::Migration
      def change
        create_table :visits do |t|
          t.text :agenda
          t.datetime commenced_at
          t.datetime concluded_at
          t.references :state
          t.references :user
        end
      end
    end
    

    【讨论】:

      【解决方案2】:

      您不能在单个模型上拥有 has_many 和 has_one 关系,在这种情况下为 state。一种解决方案是:

      创建状态的静态模型,它们不需要是数据库模型,它们可以是状态模型上的静态变量:US_STATES = {'1' =&gt; 'AK', '2' =&gt; 'AL', etc} 或者您可以使用固定装置将状态表加载到数据库中(更多复杂,因为您需要使用 rake 任务或 db:seed 任务将夹具加载到数据库中,但很好,因为您可以使用活动记录来管理模型)。

      然后您可以在定义 home_state 的用户模型上提供 home_state_id,并且访问只是 user_id 和 state_id 之间的连接。

      【讨论】:

      • 附言。您启发了我将其写成我一直想做的帖子:jedschneider.posterous.com/…
      • 谢谢!所以本质上,我可以删除 has_one 关系,而是在 User 模型中有一个名为 :home_state 的表列,它引用来自 State 静态模型的 :state_id ?我应该为访问保留 has_many :through 关系吗?
      • 太棒了,刚刚看到你的新帖子的链接。我完全是 Rails 的新手,所以会尝试将您在帖子中写的内容应用到我的家乡和州访问问题!
      • 如果你走静态列表路线,为了澄清你会创建一个模块,如下所示:rolandmai.com/%3Fp%3D100 可能会存在于 /lib/us_state.rb 然后你会将该模块包含在任何需要了解状态的模型。 class Foo
      • 您可以拥有任意多个has_many/has_one 关联,只要它们的名称不同并且代表不同的概念。示例:论坛中的用户有许多创建的线程(与 Thread.created_by 相反)以及许多订阅的线程(通过订阅中间模型)
      【解决方案3】:

      我希望每个用户都有两个属性:一个“家”状态和多个“访问”状态。

      在您的模型中,一个州可能只有一位用户 (belongs_to)。

      正确的语义是

      class User < AR::Base
        belongs_to :home_state, :class_name => "State", :foreign_key => "home_state_id", :inverse_of => :users_living
        has_and_belongs_to_many :visited_states, :through => :visits
        # ...
      end
      
      class State < AR::Base
        has_many :users_living, :class_name => "User", :inverse_of => :home_state
        # ...
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-27
        • 1970-01-01
        • 1970-01-01
        • 2017-01-06
        • 2019-03-04
        相关资源
        最近更新 更多