【问题标题】:Can model belong to STI child?模型可以属于 STI 孩子吗?
【发布时间】:2018-10-22 06:36:25
【问题描述】:

我有一个基类 Place 和多个使用 STI 约定的子类。我有一个单独的模型Post,其中belongs_toPlace 的子类之一:

class Place < ApplicationRecord
end

class SubPlace < Place
  has_many :posts, class_name: "SubPlace", foreign_key: "sub_place_id"
end

class Post < ApplicationRecord
  belongs_to :sub_place, class_name: "SubPlace", foreign_key: "sub_place_id"
end

可以使用 Rails 控制台保存新的 Post 记录,但在尝试为特定 SubPlace 查找 Posts 时出现以下错误:

ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR:  column places.sub_place_id does not exist)

有没有办法让这个工作,或者我的关联必须只与基类相关?

添加架构:

create_table "posts", force: :cascade do |t|
    t.string "title"
    t.bigint "sub_place_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["sub_place_id"], name: "index_posts_on_sub_place_id"
end

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

【问题讨论】:

  • 错误提示没有列 sub_place_id 在位置表中。你有没有?
  • @Pavan 列“sub_place_id”在 Posts 表中,因为 Post 属于 SubPlace,或者至少应该属于。出于某种原因,Rails 似乎在此列的 Place 表中查找,我不太清楚为什么。

标签: ruby-on-rails associations single-table-inheritance


【解决方案1】:

处理关联和 STI 的更好方法是将关联设置到基类:

class Place < ApplicationRecord
end

class SubPlace < Place
  has_many :posts, foreign_key: 'place_id', inverse_of: 'place'
end

class AnotherKindOfPlace < Place
  has_many :posts, foreign_key: 'place_id', inverse_of: 'place'
end

class Post < ApplicationRecord
  belongs_to :place
end

这让事情变得简单而美好,因为Post 不知道也不关心有不同种类的地方。当您访问@post.place 时,ActiveRecord 会读取places.type 列并实例化正确的子类型。

如果基础 Post 类也有关联,您只需将其写为:

class Place < ApplicationRecord
  has_many :posts, foreign_key: 'place_id', inverse_of: 'place'
end

【讨论】:

  • 确保将列 sub_place_id 重命名为 place_id
  • 感谢您解释这一点,如果我以这种方式设置关联,它是否会限制 Post 以便它只能属于 SubPlace 或 AnotherKindOfPlace,即使它是写为属于基类的?理想情况下,帖子只能属于子地点,而不能属于基础地点。
  • 不——不是。这种假设您遵循 liskov 可替代原则。 belongs_to :place 关联将允许您分配 Place 的任何子类型。
  • 如果我要将关联添加到基础“Place”类,那么我当然可以简单地放置“has_many :posts”,而不指定foreign_key 和inverse_of?这是正确的,只是想确保我做对了。如果我想确保 Post 只能属于“SubPlace”,那么我可以使用类验证来指定 - 这是遵循 LSP 约定的更好方法吗?
  • 是的,当可以从关联的名称中推断出外键时,我认为您不需要指定外键,因此has_many :posts 将使用post_id,无论类名称如何。 inverse_of 默认情况下未指定,因此它确实有所作为。
【解决方案2】:

ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column places.sub_place_id 不存在)

您在SubPlace 中的关联无效。你应该重写它只是

class SubPlace < Place
  has_many :posts
end

【讨论】:

  • 成功了 - 我的 SubPlace 视图中的一行也有一个错字,导致问题:place_path(post) - 应该是 post_path(post) - 结合起来,这已经解决了问题.谢谢帕万!
猜你喜欢
  • 2018-02-23
  • 1970-01-01
  • 2022-01-05
  • 1970-01-01
  • 2013-08-16
  • 1970-01-01
  • 2017-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多