【问题标题】:Is there an idiomatic way to cut out the middle-man in a join in Rails?有没有一种惯用的方法可以在 Rails 的连接中去掉中间人?
【发布时间】:2011-02-22 16:53:47
【问题描述】:

我们有一个 Customer 模型,它有很多 has_many 关系,例如到 CustomerCountry 和 CustomerSetting。通常,我们需要将这些关系相互连接起来;例如查找给定国家/地区的客户设置。表达这一点的正常方式是这样的

CustomerSetting.find :all,
                     :joins => {:customer => :customer_country},
                     :conditions => ['customer_countries.code = ?', 'us']

但等效的 SQL 以

结尾
SELECT ... FROM customer_settings 
INNER JOIN customers ON customer_settings.customer_id = customers.id
INNER JOIN customer_countries ON customers.id = customer_countries.customer_id

当我真正想要的是

SELECT ... FROM customer_settings
INNER JOIN countries ON customer_settings.customer_id = customer_countries.customer_id

我可以通过显式设置 :joins SQL 来做到这一点,但是有没有一种惯用的方式来指定这个连接?

【问题讨论】:

  • 您在CustomerSetting 班级中有哪些关联?
  • @William: belongs_to :customer

标签: ruby-on-rails join


【解决方案1】:

除了让我觉得你有一个完全属于一个客户的“国家”这个概念有点困难之外:

你为什么不在你的模型中添加另一个关联,这样每个设置has_manycustomer_countries。这样你就可以走了

CustomerSetting.find(:all, :joins => :customer_countries, :conditions => ...)

例如,如果您在客户和她的设置之间有一对一的关系,您也可以通过客户进行选择:

class Customer
  has_one :customer_setting
  named_scope :by_country, lambda { |country| ... }
  named_scope :with_setting, :include => :custome_setting
  ...
end

然后

Customer.by_country('us').with_setting.each do |cust|
  setting = cust.customer_setting
  ...
end

总的来说,我发现使用命名范围更优雅,更不用说范围将成为默认的查找方法,并且当前的#find API 将在 Rails 的未来版本中被弃用。

另外,不要太担心查询的性能。只修复您实际看到表现不佳的事情。 (如果您在高负载应用程序中确实有一个关键查询,您可能会以#find_by_sql 结束。但如果没关系,不要优化它

【讨论】:

  • 当你说每个设置 has_many customer_countries 时,我猜你的意思是 has_many :customer_countries, :primary_key => 'customer_id', :foreign_key => 'customer_id' ?
  • 听起来像我的想法。从未尝试过确切的设置,但理论上它可以工作。如果客户有多个设置,您必须尝试它是否也有效......再次,从 Rails 的角度来看,我更喜欢命名范围解决方案。
猜你喜欢
  • 1970-01-01
  • 2019-10-22
  • 1970-01-01
  • 1970-01-01
  • 2014-06-23
  • 1970-01-01
  • 1970-01-01
  • 2023-02-23
  • 1970-01-01
相关资源
最近更新 更多