【问题标题】:ActiveRecord inner join using name from has_many associationActiveRecord 内部连接使用来自 has_many 关联的名称
【发布时间】:2015-08-05 01:30:24
【问题描述】:

我有这两个基本的 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

我正在尝试使用ImportLog 上的INNER JOINImportJob 运行查询,使用我在has_many 声明中给出的名称:

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

但是,ActiveRecord 在构造INNER JOIN 时不会自动使用名称logs。这是它生成的 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”参考“日志”(不直观)。

    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" })
    

还有比这些更好的解决方案吗?

【问题讨论】:

  • 不,欢迎使用 ActiveRecord。 join 需要模型中的关系名称,而 where 需要表名(可能是因为这样执行起来更快)。

标签: ruby-on-rails ruby activerecord


【解决方案1】:

您必须向belongs_to 和其他关系声明提供的参数越多,您为将来设置的痛苦就越多。尽管import_job.import_logs 听起来很笨拙,但你的 AR 模型应该尽可能简单,即使牺牲了一定程度的优雅。

相反,将漂亮、优雅的 API 构建为 AR 模型和控制器之间的层。将其限制为您需要的操作,而不是ActiveRecord::Base 允许的无限操作。借此机会为您的导入系统定义一个干净、恰到好处的接口,并将 AR 模型作为实现细节。未来会分红。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-07
    • 2015-05-07
    • 1970-01-01
    • 2011-06-23
    相关资源
    最近更新 更多