虽然在轨道中具有STI,但多态有很多

时间:2017-06-13 19:59:01

标签: ruby-on-rails ruby ruby-on-rails-4 activerecord rails-activerecord

我遇到了rails关联的异常情况,并创建了简单的示例来表明这一点。 假设我们有4个型号: 1)list.rb

class List < ApplicationRecord;end

2)show.rb

class Show < List;end

3)joined_table.rb

class JoinedTable < ApplicationRecord
  belongs_to :listable, polymorphic: true
  belongs_to :work
end

4)work.rb

class Work < ApplicationRecord
  has_many :joined_tables, dependent: :destroy
  has_many :lists, through: :joined_tables, source: :listable, source_type: 'List'
  has_many :shows, through: :joined_tables, source: :listable, source_type: 'Show'
end

因此我们有下一个schema.rb

create_table "joined_tables", force: :cascade do |t|
  t.integer  "listable_id"
  t.string   "listable_type"
  t.integer  "work_id"
  t.datetime "created_at",    null: false
  t.datetime "updated_at",    null: false
end

create_table "lists", force: :cascade do |t|
  t.string   "type"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

create_table "works", force: :cascade do |t|
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

现在,当你得到所有信息时,任何人都可以解释我为什么在尝试

Work.first.shows

它会生成

 Show Load (0.2ms)  SELECT "lists".* FROM "lists" INNER JOIN "joined_tables" ON "lists"."id" = "joined_tables"."listable_id" WHERE "lists"."type" IN ('Show') AND "joined_tables"."work_id" = ? AND "joined_tables"."listable_type" = ?  [["work_id", 1], ["listable_type", "Show"]]

看起来像是一个正确的请求,但是当我试图做

Work.first.lists

它会生成

  List Load (0.1ms)  SELECT "lists".* FROM "lists" INNER JOIN "joined_tables" ON "lists"."id" = "joined_tables"."listable_id" WHERE "joined_tables"."work_id" = ? AND "joined_tables"."listable_type" = ?  [["work_id", 1], ["listable_type", "List"]]

这不是相同的并且缺少检查lists.type。

我知道这是一个糟糕的架构,人们可能应该在工作中使用范围,在JoinedTable中放弃多态,但这只是我在项目中遇到的问题。我也很乐意听到其他解决方案,但最重要的是我想知道为什么rails会产生不同的查询。

P.S。在示例中,我使用rails 5和SQLite。但是在使用rails 4和postgres的真实项目中,情况更糟。如果我先执行Work.first.shows,然后再运行Work.first.lists,则会生成

  List Load (0.4ms)  SELECT "lists".* FROM "lists" INNER JOIN "list_works" ON "lists"."id" = "list_works"."listable_id" WHERE "list_works"."work_id" = $1 AND "list_works"."listable_type" IN ('List', 'Show')  [["work_id", 50]]

但如果我立即执行Work.first.lists,则会生成

  List Load (0.8ms)  SELECT "lists".* FROM "lists" INNER JOIN "list_works" ON "lists"."id" = "list_works"."listable_id" WHERE "list_works"."work_id" = $1 AND "list_works"."listable_type" = 'List'  [["work_id", 50]]

谢谢你的帮助!

1 个答案:

答案 0 :(得分:1)

Work.first.lists没有生成lists.type,因为List是层次结构中的基类,因此列表是lists表中所有类型的所有记录。另外,请记住,所有List条记录在数据库中都有type = nil,因为List是基类。

此外,当您使用与STI的多态关联时,默认情况下始终将基类的名称分配给多态关联的*_type列。在您的情况下,listable_type的{​​{1}}始终为JoinedTable

第二个问题是因为当您首先执行"List"时,Rails并不知道有一个子Work.first.lists类,因为此时它还没有被加载。您是否应该通过访问它来进行自动加载(它发生在Show时),或者使用Work.first.shows之类的内容。

以下是适合您的解决方案:

require_dependency
相关问题