【发布时间】:2015-04-25 10:44:28
【问题描述】:
使用包含范围的模型关注点,在知道可能存在嵌套和/或自引用查询的情况下编写这些关注点的最佳方式是什么?
在我的一个顾虑中,我的范围与以下类似:
scope :current, ->(as_at = Time.now) { current_and_expired(as_at).current_and_future(as_at) }
scope :current_and_future, ->(as_at = Time.now) { where("#{upper_bound_column} IS NULL OR #{upper_bound_column} >= ?", as_at) }
scope :current_and_expired, ->(as_at = Time.now) { where("#{lower_bound_column} IS NULL OR #{lower_bound_column} <= ?", as_at) }
def self.lower_bound_column
lower_bound_field
end
def self.upper_bound_column
upper_bound_field
end
并通过 has_many 引用,例如:has_many :company_users, -> { current }
如果进行 ActiveRecord 查询,该查询引用了包含该关注点的几个模型,这会导致“不明确的列名”异常,这是有意义的。
为了帮助克服这个问题,我将列名辅助方法更改为现在
def self.lower_bound_column
"#{self.table_name}.#{lower_bound_field}"
end
def self.upper_bound_column
"#{self.table_name}.#{upper_bound_field}"
end
这很好用,直到您需要自引用查询。 Arel 通过在生成的 SQL 中为表名设置别名来帮助缓解这些问题,例如:
LEFT OUTER JOIN "company_users" "company_users_companies" ON "company_users_companies"."company_id" = "companies"."id"
和
INNER JOIN "company_users" ON "users"."id" = "company_users"."user_id" WHERE "company_users"."company_id" = $2
这里的问题是self.table_name 不再引用查询中的表名。这导致舌头在脸颊提示:HINT: Perhaps you meant to reference the table alias "company_users_companies"
为了将这些查询迁移到 Arel,我将列名辅助方法更改为:
def self.lower_bound_column
self.class.arel_table[lower_bound_field.to_sym]
end
def self.upper_bound_column
self.class.arel_table[upper_bound_field.to_sym]
end
并更新了范围以反映:
lower_bound_column.eq(nil).or(lower_bound_column.lteq(as_at))
但这只是移植了问题,因为无论查询如何,self.class.arel_table 都将始终相同。
我想我的问题是,如何创建可用于自引用查询的范围,这需要 <= 和 >= 等运算符?
编辑
我创建了一个基本应用程序来帮助展示这个问题。
git clone git@github.com:fattymiller/expirable_test.git
cd expirable_test
createdb expirable_test-development
bundle install
rake db:migrate
rake db:seed
rails s
发现和假设
- 在 sqlite3 中工作,而不是 Postgres。很可能是因为 Postgres 强制执行 SQL 中的查询顺序?
【问题讨论】:
-
我也有类似的问题。在这里查看我的问题:stackoverflow.com/questions/28595636/…
标签: ruby-on-rails activerecord arel