【问题标题】:Rails 3 has_many :through + join table conditions / scopingRails 3 has_many:通过+连接表条件/范围
【发布时间】:2011-10-13 11:06:15
【问题描述】:

我正在开发一个具有UserProject 模型的应用程序,User 可以通过ProjectUser 分配给多个Projects,具有角色(例如开发人员、设计师) .

Project
  has_many :project_users
  has_many :users, :through => :project_users

User
  has_many :project_users
  has_many :projects, :through => :project_users

ProjectUser (user_id, project_id, role)
  belongs_to :user
  belongs_to :project

我可以打电话给@project.users@user.projects,但由于角色不同,我想更具体一点的关系。理想情况下,我希望能够做到以下几点:

@project.developers
  # returns @project.users, but only where ProjectUser.role = 'Developer'

@project.designers << @user
  # creates a ProjectUser for @project, @user with role 'Designer'

@user.development_projects
  # returns projects where @user is assigned as a 'Developer'

@user.design_projects << @project
  # creates a ProjectUser for @project, @user with role 'Designer'

我目前有以下代码:

has_many :developers, :through => :project_users, :source => :user,
                      :class_name => "User",
                      :conditions => ['project_users.role = ?','Developer']

但这只是真正的单向获取,并没有给我太多其他东西 - 我无法构建或分配或任何东西。

我正在尝试一些我认为可能可行的更复杂的逻辑,但希望得到一些建议:

has_many :developer_assignments, :source => :project_user,
                                 :conditions => { :role => 'Developer' }
has_many :developers, :through => :developer_assignments # class_name?

有什么建议吗?谢谢!

【问题讨论】:

    标签: ruby-on-rails-3 activerecord arel


    【解决方案1】:

    has_many 接受可以定义/覆盖关联方法的块。这将允许您为&lt;&lt; 创建自定义方法。我为你创建了一个小例子,你可以用类似的方式创建构建。

    # Project.rb
    has_many :developers, :through => :project_users, :source => :user,
             :conditions => "project_users.role = 'developer'" do
             def <<(developer)
               proxy_owner.project_users.create(:role => 'developer', :user => developer)
             end
           end
    

    现在您可以根据要求向您的项目添加新的开发人员:@project.developers &lt;&lt; @user@project.developers 为您提供所有开发人员。

    如果您有很多角色,动态创建这些 has_many 语句可能会很有用。

    # Project.rb
    ROLES = ['developer','contractor']
    
    ROLES.each do |role|         
      self.class_eval <<-eos
        has_many :#{role.downcase}s, :through => :project_users, :source => :user,
               :conditions => "project_users.role = '#{role}'" do
                 def <<(user)
                   proxy_owner.project_users.create(:role => '#{role}', :user => user)
                 end
               end
      eos
    end
    

    回顾它上面的一切似乎不像 rails 方式 做事。对此进行范围界定应该可以在不重新定义所有内容的情况下使构建和创建命令正常工作。

    希望这会有所帮助!

    【讨论】:

    • 感谢您的回答。它解决了我的问题,但并没有像我希望的那样 - 通过在ProjectUsers 模型上使用范围。声明 :conditions =&gt; "project_users.role = '#{role}'" 似乎不是很随意,因为我希望调用类似 :conditions =&gt; { :scope =&gt; :developer } 的名称。我仍然确定这是可能的。无论哪种方式,我都会奖励您的努力,尽管此答案不会被标记为正确。感谢您的意见!
    • 谢谢。我真的希望has_many :designers, :through =&gt; :project_users, :source =&gt; :user, :conditions =&gt; {:project_users =&gt; {:role =&gt; :designer}} 能够自动工作,但显然嵌套条件散列不受构建和创建范围的限制。这是我能想到的最好的了。
    • 上面的proxy_owner是什么?是对关联另一方的引用,类似于您使用:extend 获得的proxy_association
    • proxy_owner 指的是(在这种情况下)Project 对象的实例。
    【解决方案2】:

    听起来您正在寻找的是 RoR 的 single table inheritancenamed scopes 的组合。

    看看下面的article 是一个关于多态关联的好例子。这应该可以帮助您实现以下目标:

    @project.developers
      # returns @project.users, but only where ProjectUser.role = 'Developer'
    
    @project.designers << @user
      # creates a ProjectUser for @project, @user with role 'Designer'
    

    Scopes 将为您提供一种简洁的方式来实现 @user.development_projects,但可能需要更多技巧才能获得 &lt;&lt; 运算符。

    【讨论】:

      【解决方案3】:

      您尝试过使用scopes 了吗?它不允许你做

      试试:

      Project
        scope :developers, lambda {
          includes(:project_users).where("project_users.role = ?", "developer")
        }
      

      您将能够让所有开发人员使用:@project.developers

      【讨论】:

        猜你喜欢
        • 2023-03-10
        • 1970-01-01
        • 2023-04-04
        • 1970-01-01
        • 1970-01-01
        • 2016-01-07
        • 2015-05-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多