我有一个网站,允许用户通过多种服务(LinkedIn,电子邮件,Twitter等)登录。
我设置了以下结构来模拟User
及其多个身份。基本上,用户可以具有多个身份,但只有一个给定类型(例如,不能有2个Twitter身份)。
我决定将其设置为多态关系,如下所示。基本上,中间表identities
将User
条目映射到多个*_identity
表。
关联如下(仅针对LinkedInIdentity
显示,但可以推断)
# /app/models/user.rb
class User < ActiveRecord::Base
has_many :identities
has_one :linkedin_identity, through: :identity, source: :identity, source_type: "LinkedinIdentity"
...
end
# /app/models/identity
class Identity < ActiveRecord::Base
belongs_to :user
belongs_to :identity, polymorphic: true
...
end
# /app/models/linkedin_identity.rb
class LinkedinIdentity < ActiveRecord::Base
has_one :identity, as: :identity
has_one :user, through: :identity
...
end
我遇到的问题是使用User
模型。由于它可以有多个身份,我使用has_many :identities
。但是,对于给定的身份类型(例如LinkedIn),我使用了has_one :linkedin_identity ...
。
问题是has_one
语句是through: :identity
,并且没有名为:identity
的单一关联。只有复数:identities
> User.first.linkedin_identity
ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association :identity in model User
有什么方法吗?
答案 0 :(得分:3)
我会这样做 - 我已经将身份和其他人之间的关系名称更改为external_identity
,因为说identity.identity
只是令人困惑,特别是当你不这样做时获取身份记录。我还在Identity上进行了唯一性验证,这将阻止为任何用户创建相同类型的第二个身份。
class User < ActiveRecord::Base
has_many :identities
has_one :linkedin_identity, through: :identity, source: :identity, source_type: "LinkedinIdentity"
end
# /app/models/identity
class Identity < ActiveRecord::Base
#fields: user_id, external_identity_id
belongs_to :user
belongs_to :external_identity, polymorphic: true
validates_uniqueness_of :external_identity_type, :scope => :user_id
...
end
# /app/models/linkedin_identity.rb
class LinkedinIdentity < ActiveRecord::Base
# Force the table name to be singular
self.table_name = "linkedin_identity"
has_one :identity
has_one :user, through: :identity
...
end
编辑 - 而不是为linkedin_identity建立关联,您可以随时使用getter和setter方法。
#User
def linkedin_identity
(identity = self.identities.where(external_identity_type: "LinkedinIdentity").includes(:external_identity)) && identity.external_identity
end
def linkedin_identity_id
(li = self.linkedin_identity) && li.id
end
def linkedin_identity=(linkedin_identity)
self.identities.build(external_identity: linkedin_identity)
end
def linkedin_identity_id=(li_id)
self.identities.build(external_identity_id: li_id)
end
EDIT2 - 重构了以上内容以使其更加形式友好:您可以将linkedin_identity_id =方法用作&#34;虚拟属性&#34;,例如,如果您有一个类似"user[linkedin_identity_id]"
的表单字段,如果是LinkedinIdentity的id,则可以通常的方式在控制器中执行@user.update_attributes(params[:user])
。
答案 1 :(得分:1)
这是一个在这里完美地运作的想法。 (我的情况有点不同,因为所有标识都在同一个表中,相同基类型的子类)。
class EmailIdentity < ActiveRecord::Base
def self.unique_for_user
false
end
def self.to_relation
'emails'
end
end
class LinkedinIdentity < ActiveRecord::Base
def self.unique_for_user
true
end
def self.to_relation
'linkedin'
end
end
class User < ActiveRecord::Base
has_many :identities do
[LinkedinIdentity EmailIdentity].each do |klass|
define_method klass.to_relation do
res = proxy_association.select{ |identity| identity.is_a? klass }
res = res.first if klass.unique_for_user
res
end
end
end
end
然后你可以
@user.identities.emails
@user.identities.linkedin