ruby-on-rails – 带有别名表名的ActiveRecord查询

前端之家收集整理的这篇文章主要介绍了ruby-on-rails – 带有别名表名的ActiveRecord查询前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
使用包含范围的模型关注点,知道嵌套和/或自引用查询的最佳方法是什么?

在我的一个问题中,我的范围与以下类似:

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”

为了将这些查询迁移到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(无).或者(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中强制执行查询顺序?

解决方法

好吧,好吧.经过相当大的时间查看Arel,ActiveRecord和Rails问题的来源(似乎这不是新的),我能够找到访问当前arel_table对象的方法,如果正在使用它们的table_aliases,执行时的当前范围.

这使得可以知道范围是否将在具有别名的表名的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 issueother one.
>和the commit on your test app在github上进行了绿色测试!

猜你在找的Ruby相关文章