在我的一个问题中,我的范围与以下类似:
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”=“公司”.“id”
和
INNER JOIN“company_users”ON“users”.“id”=“company_users”.“user_id”WHERE“company_users”.“company_id”= $2
这里的问题是self.table_name不再引用查询中的表名.这导致舌头在脸颊暗示:提示:也许你的意思是引用表别名“company_users_companies”
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(无).或者(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
调查结果和假设
解决方法
这使得可以知道范围是否将在具有别名的表名的JOIN中使用,或者另一方面可以在实际表名上使用范围.
def self.current_table_name current_table = current_scope.arel.source.left case current_table when Arel::Table current_table.name when Arel::Nodes::TableAlias current_table.right else fail end end
正如您所看到的,我使用current_scope
作为基础对象来查找arel表,而不是先前尝试使用self.class.arel_table甚至是relation.arel_table,正如您所说的那样,无论在哪里都保持不变范围被使用.我只是在该对象上调用source来获取Arel::SelectManager
,而Arel::SelectManager
又会在#left上显示当前表.此时有两个选项:你有一个Arel :: Table(没有别名,表名在#name上)或者你有一个Arel :: Nodes :: TableAlias和#right上的别名.
使用该table_name,您可以在您的范围内恢复为#{current_table_name}.#{lower_bound_field}和#{current_table_name}.#{upper_bound_field}的首次尝试:
def self.lower_bound_column "#{current_table_name}.#{lower_bound_field}" end def self.upper_bound_column "#{current_table_name}.#{upper_bound_field}" end scope :current_and_future,as_at) }
在我看来,这个current_table_name方法对AR / Arel公共API有用,因此可以跨版本升级进行维护.你怎么看?
如果你有兴趣,这里有一些我在路上使用的参考:
>一个similar question on SO,回答了大量的代码,你可以使用而不是你美丽而简洁的能力.
>这Rails issue和other one.
>和the commit on your test app在github上进行了绿色测试!