has_many:through和default_scope的奇怪行为

时间:2015-04-13 19:20:50

标签: ruby-on-rails ruby-on-rails-3 activerecord has-many-through default-scope

我们通过以下方式获得了基本的has_many:用户和公司。

class User < ActiveRecord::Base
  extends CompanyMethods
  has_many :company_users
  has_many :companies, through: company_users

  has_many :orders
end

class CompanyUser < ActiveRecord::Base
  belongs_to :user
  belongs_to :company
  validates_uniqueness_of :company_id, scope: :user_id
end

class Company < ActiveRecord::Base
  has_many :company_users
  has_many :users, through: company_users
end

然后我们有这个CompanyMethods lib,它向用户添加default_scope

module CompanyMethods
  def self.extended(klass)
    klass.class_eval do
      default_scope do
        c_ids = Authorization.current_company_ids
        includes(:companies).where(companies: { id: c_ids })
      end
    end
  end
end

因此,当我们致电User.find_by_email('michael@widgetworks.com')时,我们只会收回Authorization.current_company_ids范围内的用户。

这是sql:

SELECT DISTINCT `users`.id FROM `users` LEFT OUTER JOIN `company_users` ON `company_users`.`user_id` = `users`.`id` LEFT OUTER JOIN `companies` ON `companies`.`id` = `company_users`.`company_id` WHERE `companies`.`id` IN (4) AND `users`.`email` = 'michael@widgetworks.com' LIMIT 1

对于很多实例而言,这一切都很好。但是这里开始变得时髦。

当另一个对象(例如CreditCard调用通过User的关联)时,会在子对象上调用companies.id范围。

class CreditCard < ActiveRecord::Base
  has_one :user, through: :user_profile
  has_many :orders, through: :user
end

class UserProfile < ActiveRecord::Base
  belongs_to :user
end

这是它生成的sql:

SELECT `orders`.* FROM `orders` INNER JOIN `users` ON `orders`.`user_id` = `users`.`id` INNER JOIN `user_profiles` ON `users`.`id` = `user_profiles`.`user_id` WHERE `orders`.`company_id` IN (4) AND `companies`.`id` IN (4) AND `user_profiles`.`id` = 47717

credit_card.orders抛出一个错误,因为它的SQL查询在companies上调用“idreservation IN(4)”时只应在user上调用它{1}}。

我已经能够通过hack而不是使用through关联解决问题,我刚刚在CreditCard上编写了一个名为reservations的方法。

class CreditCard < ActiveRecord::Base
  has_one :user, through: :user_profile

  def orders
    user.orders
  end
end

这几乎解决了这个问题,但它并不是一个很好的解决方案。

0 个答案:

没有答案