使用has_many关联名称的ActiveRecord内连接

时间:2015-08-05 01:30:25

标签: ruby-on-rails ruby activerecord

我有两个基本的ActiveRecord模型(Rails 4.2):

class ImportJob < ActiveRecord::Base
  has_many :logs, class_name: 'ImportLog', foreign_key: 'job_id', dependent: :destroy
end

class ImportLog < ActiveRecord::Base
  belongs_to :job, class_name: 'ImportJob', foreign_key: :job_id
end

我正在尝试使用ImportJob上的INNER JOINImportLog上运行查询,使用我在has_many声明中提供的名称:

ImportJob.joins(:logs).where(logs: { stage: "load", status: "succeeded" })

但是,构建logs时,ActiveRecord不会自动使用名称INNER JOIN。这是它生成的SQL的错误:

PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "logs"
LINE 1: ..."import_logs"."job_id" = "import_jobs"."id" WHERE "logs"."st...
                                                             ^
: SELECT "import_jobs".* FROM "import_jobs" INNER JOIN "import_logs" ON "import_logs"."job_id" = "import_jobs"."id" WHERE "logs"."stage" = $1 AND "logs"."status" = $2

这两个查询都有效,但它们似乎不直观或不优雅:

  1. 使用表名“import_logs”(非直观)来引用“logs”。

    ImportJob.joins(:logs).where(import_logs: { stage: "load", status: "succeeded" })
    
  2. 自己编写INNER JOIN并添加“AS日志”(不太优雅)。

    ImportJob.joins("INNER JOIN import_logs AS logs ON logs.job_id = import_jobs.id")
         .where(logs: { stage: "load", status: "succeeded" })
    
  3. 有比这些更好的解决方案吗?

1 个答案:

答案 0 :(得分:0)

您必须为belongs_to和其他关系声明提供的论据越多,您将来的痛苦就越多。与import_job.import_logs听起来一样笨重,你的AR模型应该尽可能简单,即使是以一定程度的优雅为代价。

相反,将您漂亮,优雅的API构建为AR模型和控制器之间的层。将其限制为您需要的操作,而不是ActiveRecord::Base允许的无限制操作。利用这个机会为您的导入系统定义一个干净,足够的界面,将AR模型作为实现细节。它将在未来支付股息。