我见过许多多对多关联,但似乎共同的趋势是他们最终使用has_many:通过关系。
假设您有以下内容:
class User < ActiveRecord::Base
has_many :user_relationships
has_many :relations, :through => :user_relationships
end
class UserRelationship < ActiveRecord::Base
belongs_to :user
belongs_to :related_user, class_name: "User"
end
鉴于此类关系,您最终将关系设置如下:
user1.relations << user2
user2.relations << user1
或者这个:
user1.user_relationships.build(related_user_id: 2)
user2.user_relationships.build(related_user_id: 1)
导致连接表中的行如下所示:
user_id | related_user_id
1 | 2
2 | 1
因此,在设置上述关系时,您可以看到可以完成以下任务
user1.relations.include? user2 = true
user2.relations.include? user1 = true
我的问题是:在Rails中是否有办法完成上述任务,或至少与上述内容类似,不必为每个单向双向关系创建2行,并保持查看关系的能力从两端以有效的方式,将创建这种关系的空间复杂性减少一半......
道歉,如果这是一个noobie问题,但我是Rails的新手,只是开始了解事情。很容易找到如何设置它们,但我发现更难以找到如何以有效的方式实际实现它们
答案 0 :(得分:0)
这是(大致)我所做的。我将跳过大量细节,但如果它有用,我很乐意分享更多细节。这可能很令人发指,所以我很好奇别人的想法。
基本上,我有Relationship
模型,如:
module ActsAsRelatingTo
class Relationship < ActiveRecord::Base
validates :owner_id, presence: true
validates :owner_type, presence: true
validates :in_relation_to_id, presence: true
validates :in_relation_to_type, presence: true
belongs_to :owner, polymorphic: true
belongs_to :in_relation_to, polymorphic: true
acts_as_taggable
acts_as_taggable_on :roles
end
end
然后,我创建了一个acts_as_relating_to模块(再次,掩盖了细节),其中包括:
module ActsAsRelatingTo
def acts_as_relating_to(*classes_array)
# re-opens the class at run time so I can do things like add
# new instance methods on-the-fly
class_eval do
before_destroy :tell_to_unrelate
has_many :owned_relationships,
as: :owner,
class_name: "ActsAsRelatingTo::Relationship",
dependent: :destroy
has_many :referencing_relationships,
as: :in_relation_to,
class_name: "ActsAsRelatingTo::Relationship",
dependent: :destroy
# iterates through arguments passed in 'acts_as_relating_to' call
# in the Person model, below.
classes_array.each do |class_sym|
# This is a method created on-the-fly. So, when I call
# (in the Person model, below) 'acts_as_relating_to :people',
# then I get a method on each instance of 'Person' called
# 'people_that_relate_to_me. You can also create class methods
# using `define_singleton_method`.
#
# The reason for doing all of this via the define_method
# is that it lets me create any number of 'things_i_relate_to'
# methods on any class descending from ActiveRecord::Base
# (more on this in a bit). Which, if I understand it
# correctly, is how a lot of the ActiveRecord functionality gets
# into a model in the first place.
define_method(class_sym.to_s + "_that_relate_to_me") do |options={}|
... some stuff
end
# Same as above, but know I'm defining a method called
# 'people_i_relate_to'
define_method(class_sym.to_s+"_i_relate_to") do |options={}|
... some more stuff
end
# I can also create static methods and incorporate them in the
# 'Person' class. I just define them in modules (such as
# ActsAsRelatingTo::InstanceMethods and ActsAsRelatingTo::ClassMethods)
# and then either 'include' (for instance methods) or 'extend'
# (for class methods) them.
include InstanceMethods
extend ClassMethods
end
end
end
end
# Here, I'm telling ActiveRecord::Base to 'extend' this module.
# That makes the 'acts_as_relating_to' method available in
# any class that descends from ActiveRecord::Base.
ActiveRecord::Base.extend ActsAsRelatingTo
然后,我可以做类似的事情:
class Person < ActiveRecord::Base
# Here, I am calling the method that I defined above, passing in
# :people, :organizations, and :programs. This is exactly the
# sort of thing you do all the time when you say something like
# 'has_one :foo', or 'belongs_to :bar'.
acts_as_relating_to :people, :organizations, :programs
# Here, I am calling a method I have that builds on acts_as_relating_to,
# but which I did not show, that creates administrative methods on
# the person so that I can say stuff like 'person.administrate organization'.
# Or, 'organization.administrators'.
acts_as_administering :organizations, :programs
...
end
所以,如果我有person_1
和person_2
并且我有一个关系记录owner
是person_1
,in_relation_to
是{{1}然后我可以说person_2
然后回到person_1.people_i_relate_to
。或者,我可以说,person_2
并返回person_2.people_that_relate_to_me
。
我还推出了基于person_1
模块构建的acts_as_administering
模块,让我可以执行acts_as_relating_to
和person_1.administered_organizations
等内容。
我可能已经喋喋不休了太长时间。无论如何,如果它很有趣,我可以说更多。
干杯!