ruby-on-rails - 无法让 STI 在模型上充当多态关联
问题描述
我有一个用户模型,它可以有一个电子邮件和一个电话号码,这两个都是他们自己的模型,因为它们都需要某种形式的验证。
所以我想做的是附加Verification::EmailVerification
asemail_verifications
和Verification::PhoneVerification
as phone_verifications
,它们都是Verification
.
class User < ApplicationRecord
has_many :email_verifications, as: :initiator, dependent: :destroy
has_many :phone_verifications, as: :initiator, dependent: :destroy
attr_accessor :email, :phone
def email
@email = email_verifications.last&.email
end
def email=(email)
email_verifications.new(email: email)
@email = email
end
def phone
@phone = phone_verifications.last&.phone
end
def phone=(phone)
phone_verifications.new(phone: phone)
@phone = phone
end
end
class Verification < ApplicationRecord
belongs_to :initiator, polymorphic: true
end
class Verification::EmailVerification < Verification
alias_attribute :email, :information
end
class Verification::PhoneVerification < Verification
alias_attribute :phone, :information
end
但是,通过上述设置,我得到了错误uninitialized constant User::EmailVerification
。我不确定我要去哪里错了。
email_verifications
我如何构建它以便我可以访问phone_verifications
用户模型?
解决方案
使用 STI 时,您不需要(或想要)多态关联。
多态关联是围绕对象关系阻抗不匹配问题的一种破解方法,用于设置指向多个表的单个关联。例如:
class Video
has_many :comments, as: :commentable
end
class Post
has_many :comments, as: :commentable
end
class Comment
belongs_to :commentable, polymorphic: true
end
应该谨慎使用它们的原因是没有参照完整性,并且存在许多与 STI 没有的连接和急切加载记录相关的问题,因为您有一个指向单个表的“真实”外键列。
Rails 中的 STI 仅使用 ActiveRecord 读取type
列以查看在加载记录时要实例化哪个类的事实,这也用于多态关联。否则它与多态无关。
当您设置与 STI 模型的关联时,您只需创建与基本继承类的关联,rails 将在加载关联记录时通过读取类型列来处理类型解析:
class User < ApplicationRecord
has_many :verifications
end
class Verification < ApplicationRecord
belongs_to :user
end
module Verifications
class EmailVerification < ::Verification
alias_attribute :email, :information
end
end
module Verifications
class PhoneVerification < ::Verification
alias_attribute :email, :information
end
end
您还应该将模型嵌套在模块而不是类中。这部分是由于模块查找中的错误直到 Ruby 2.5 才解决,也由于约定。
如果您想为验证的子类型创建更具体的关联,您可以通过以下方式完成:
class User < ApplicationRecord
has_many :verifications
has_many :email_verifications, ->{ where(type: 'Verifications::EmailVerification') },
class_name: 'Verifications::EmailVerification'
has_many :phone_verifications, ->{ where(type: 'Verifications::PhoneVerification') },
class_name: 'Verifications::PhoneVerification'
end
如果你想给关联起别名user
并调用它,initiator
你可以通过向关联提供类名选项并在belongs_to
关联中指定外键来实现has_many
:
class Verification < ApplicationRecord
belongs_to :initiator, class_name: 'User'
end
class User < ApplicationRecord
has_many :verifications, foreign_key: 'initiator_id'
has_many :email_verifications, ->{ where(type: 'Verifications::EmailVerification') },
class_name: 'Verifications::EmailVerification',
foreign_key: 'initiator_id'
has_many :phone_verifications, ->{ where(type: 'Verifications::PhoneVerification') },
class_name: 'Verifications::PhoneVerification',
foreign_key: 'initiator_id'
end
但是,这与多态性无关。
推荐阅读
- powershell - Jenkinsfile Powershell - 从一个会话到另一个会话的变量?
- javascript - create-react-app 中如何导入自制模块?
- orchardcms - 有没有办法在放置和渲染发生之前改变 Orchard 内容形状的显示类型?
- html - 为我的网站制作简单的图像滑块
- templates - 模板中除最后一项之外的所有内容的 Wagtail 基本菜单分隔符
- kubernetes - 由于 GCP 服务帐户,Pod 在 Kubernetes 中获得了 CrashLoopBackOff
- sql-server - 计数日的 T-SQL 脚本
- python - 如何使对象在 tkinter 中不断移动
- html - 响应式缩放绝对定位的元素
- jquery - 有没有办法旋转 HTML 选择框的内容?