【问题标题】:Can't get STI to act as polymorphic association on model无法让 STI 在模型上充当多态关联
【发布时间】:2019-05-03 19:39:00
【问题描述】:

我有一个用户模型,它可以有一个电子邮件和一个电话号码,这两个都是他们自己的模型,因为它们都需要某种形式的验证。

所以我要做的是将Verification::EmailVerification 附加为email_verificationsVerification::PhoneVerification 作为phone_verifications,它们都是Verification 的STI。

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_verificationsphone_verifications

【问题讨论】:

  • 您的用户模型是多态的还是只有一个用户模型?

标签: ruby-on-rails polymorphic-associations sti


【解决方案1】:

使用 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

您还应该将模型嵌套在模块而不是类中。这部分是由于 bug in module lookup 直到 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

这与多态性无关。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-26
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 2011-09-28
    • 2014-12-16
    • 1970-01-01
    相关资源
    最近更新 更多