如何使用多态 belongs_to 关联在 Rails 中设置 MTI?
How do you set up MTI in Rails with a polymorphic belongs_to association?
为了创建一个 Short, Self Contained, Correct (Compilable), Example,假设我想执行以下操作。
我有一个博客网站。有两种类型的帖子,TextPost
和 LinkPost
。还有两种类型的用户,User
和 Guest
。我想用 TextPost
和 LinkPost
实现 Multiple Table Inheritance,我的意思是(希望我正确使用了这个词):
- 在模型级别,我将有
Post
、TextPost
和 LinkPost
。 TextPost
和 LinkPost
将继承自 Post
。
- 在数据库级别,我将为
TextPost
和 LinkPost
的“叶”模型创建表,但不会为 Post
。
每种 Post
可以属于 User
或 Guest
。所以我们有一个多态的belongs_to
情况。
我的问题是如何实现这些目标。
我tried下面的,但是不行
class Post < ApplicationRecord
self.abstract_class = true
belongs_to :author, polymorphic: true # user or guest
validates :title, :author_id, :author_type, presence: true
end
class TextPost < Post
validates :content, presence: :true
end
class LinkPost < Post
validates :url, presence: :true
end
class User < ApplicationRecord
has_many :text_posts, as: :author
has_many :link_posts, as: :author
validates :name, presence: true
end
class Guest < ApplicationRecord
has_many :text_posts, as: :author
has_many :link_posts, as: :author
end
class CreateTextPosts < ActiveRecord::Migration[6.1]
def change
create_table :text_posts do |t|
t.string :title
t.string :content
t.references :author, polymorphic: true
t.timestamps
end
end
end
class CreateLinkPosts < ActiveRecord::Migration[6.1]
def change
create_table :link_posts do |t|
t.string :title
t.string :url
t.references :author, polymorphic: true
t.timestamps
end
end
end
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.string :name
t.timestamps
end
end
end
class CreateGuests < ActiveRecord::Migration[6.1]
def change
create_table :guests do |t|
t.timestamps
end
end
end
控制台输出:
:001 > user = User.create(name: 'alice')
(1.6ms) SELECT sqlite_version(*)
TRANSACTION (0.1ms) begin transaction
TRANSACTION (0.1ms) SAVEPOINT active_record_1
User Create (1.2ms) INSERT INTO "users" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "alice"], ["created_at", "2021-06-11 23:33:38.445387"], ["updated_at", "2021-06-11 23:33:38.445387"]]
TRANSACTION (0.2ms) RELEASE SAVEPOINT active_record_1
:002'> text_post = TextPost.create(title: 'foo', content: 'lorem ipsum', author_id: 1, author_type:
'user')
Traceback (most recent call last):
1: from (irb):2:in `<main>'
NameError (wrong constant name user)
常量的名称类似于局部变量的名称,只是它们以大写字母开头。
所有内置的classes,连同你定义的classes,都有一个与class同名的对应全局常量称为 class name
.
所以在你的情况下,当你定义 User
class 时,有一个常量 class name
:User
,但不是 user
,这就是为什么出现错误 NameError (wrong constant name user)
。
尝试text_post = TextPost.create(title: 'foo', content: 'lorem ipsum', author_id: 1, author_type: 'User')
为了创建一个 Short, Self Contained, Correct (Compilable), Example,假设我想执行以下操作。
我有一个博客网站。有两种类型的帖子,TextPost
和 LinkPost
。还有两种类型的用户,User
和 Guest
。我想用 TextPost
和 LinkPost
实现 Multiple Table Inheritance,我的意思是(希望我正确使用了这个词):
- 在模型级别,我将有
Post
、TextPost
和LinkPost
。TextPost
和LinkPost
将继承自Post
。 - 在数据库级别,我将为
TextPost
和LinkPost
的“叶”模型创建表,但不会为Post
。
每种 Post
可以属于 User
或 Guest
。所以我们有一个多态的belongs_to
情况。
我的问题是如何实现这些目标。
我tried下面的,但是不行
class Post < ApplicationRecord
self.abstract_class = true
belongs_to :author, polymorphic: true # user or guest
validates :title, :author_id, :author_type, presence: true
end
class TextPost < Post
validates :content, presence: :true
end
class LinkPost < Post
validates :url, presence: :true
end
class User < ApplicationRecord
has_many :text_posts, as: :author
has_many :link_posts, as: :author
validates :name, presence: true
end
class Guest < ApplicationRecord
has_many :text_posts, as: :author
has_many :link_posts, as: :author
end
class CreateTextPosts < ActiveRecord::Migration[6.1]
def change
create_table :text_posts do |t|
t.string :title
t.string :content
t.references :author, polymorphic: true
t.timestamps
end
end
end
class CreateLinkPosts < ActiveRecord::Migration[6.1]
def change
create_table :link_posts do |t|
t.string :title
t.string :url
t.references :author, polymorphic: true
t.timestamps
end
end
end
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.string :name
t.timestamps
end
end
end
class CreateGuests < ActiveRecord::Migration[6.1]
def change
create_table :guests do |t|
t.timestamps
end
end
end
控制台输出:
:001 > user = User.create(name: 'alice')
(1.6ms) SELECT sqlite_version(*)
TRANSACTION (0.1ms) begin transaction
TRANSACTION (0.1ms) SAVEPOINT active_record_1
User Create (1.2ms) INSERT INTO "users" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "alice"], ["created_at", "2021-06-11 23:33:38.445387"], ["updated_at", "2021-06-11 23:33:38.445387"]]
TRANSACTION (0.2ms) RELEASE SAVEPOINT active_record_1
:002'> text_post = TextPost.create(title: 'foo', content: 'lorem ipsum', author_id: 1, author_type:
'user')
Traceback (most recent call last):
1: from (irb):2:in `<main>'
NameError (wrong constant name user)
常量的名称类似于局部变量的名称,只是它们以大写字母开头。
所有内置的classes,连同你定义的classes,都有一个与class同名的对应全局常量称为
class name
.
所以在你的情况下,当你定义 User
class 时,有一个常量 class name
:User
,但不是 user
,这就是为什么出现错误 NameError (wrong constant name user)
。
尝试text_post = TextPost.create(title: 'foo', content: 'lorem ipsum', author_id: 1, author_type: 'User')