我遇到了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]]
谢谢你的帮助!
答案 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