Tru*_* Lê 41 activerecord ruby-on-rails polymorphic-associations
我有一个多态关联和STI的案例.
# app/models/car.rb
class Car < ActiveRecord::Base
belongs_to :borrowable, :polymorphic => true
end
# app/models/staff.rb
class Staff < ActiveRecord::Base
has_one :car, :as => :borrowable, :dependent => :destroy
end
# app/models/guard.rb
class Guard < Staff
end
Run Code Online (Sandbox Code Playgroud)
为了使多态关联起作用,根据多态关联的API文档,http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations 我必须设置borrowable_type为base_classSTI模型,在我的情况下是Staff.
问题是:如果borrowable_type设置为STI类,为什么它不起作用?
一些测试来证明它:
# now the test speaks only truth
# test/fixtures/cars.yml
one:
name: Enzo
borrowable: staff (Staff)
two:
name: Mustang
borrowable: guard (Guard)
# test/fixtures/staffs.yml
staff:
name: Jullia Gillard
guard:
name: Joni Bravo
type: Guard
# test/units/car_test.rb
require 'test_helper'
class CarTest < ActiveSupport::TestCase
setup do
@staff = staffs(:staff)
@guard = staffs(:guard)
end
test "should be destroyed if an associated staff is destroyed" do
assert_difference('Car.count', -1) do
@staff.destroy
end
end
test "should be destroyed if an associated guard is destroyed" do
assert_difference('Car.count', -1) do
@guard.destroy
end
end
end
Run Code Online (Sandbox Code Playgroud)
但它似乎只有员工实例才是真的.结果是:
# Running tests:
F.
Finished tests in 0.146657s, 13.6373 tests/s, 13.6373 assertions/s.
1) Failure:
test_should_be_destroyed_if_an_associated_guard_is_destroyed(CarTest) [/private/tmp/guineapig/test/unit/car_test.rb:16]:
"Car.count" didn't change by -1.
<1> expected but was
<2>.
Run Code Online (Sandbox Code Playgroud)
谢谢
0x4*_*672 33
好问题.我使用Rails 3.1时遇到了完全相同的问题.看起来你不能这样做,因为它不起作用.可能这是一种预期的行为.显然,在Rails中结合使用多表关联和单表继承(STI)有点复杂.
Rails 3.2的当前Rails文档提供了结合多态关联和STI的建议:
将多态关联与单表继承(STI)结合使用有点棘手.为了使关联按预期工作,请确保将STI模型的基本模型存储在多态关联的类型列中.
在您的情况下,基本模型将是"Staff",即"borrowable_type"应该是所有项目的"Staff",而不是"Guard".通过使用"变成",可以使派生类显示为基类:guard.becomes(Staff).可以将列"borrowable_type"直接设置为基类"Staff",或者如Rails文档建议的那样,将其自动转换为
class Car < ActiveRecord::Base
..
def borrowable_type=(sType)
super(sType.to_s.classify.constantize.base_class.to_s)
end
Run Code Online (Sandbox Code Playgroud)
alk*_*fee 15
一个较老的问题,但Rails 4中的问题仍然存在.另一种选择是动态地创建/覆盖该_type方法.如果您的应用程序使用与STI的多个多态关联并且您希望将逻辑保留在一个位置,这将非常有用.
这种关注将获取所有多态关联并确保始终使用基类保存记录.
# models/concerns/single_table_polymorphic.rb
module SingleTablePolymorphic
extend ActiveSupport::Concern
included do
self.reflect_on_all_associations.select{|a| a.options[:polymorphic]}.map(&:name).each do |name|
define_method "#{name.to_s}_type=" do |class_name|
super(class_name.constantize.base_class.name)
end
end
end
end
Run Code Online (Sandbox Code Playgroud)
然后将其包含在您的模型中:
class Car < ActiveRecord::Base
belongs_to :borrowable, :polymorphic => true
include SingleTablePolymorphic
end
Run Code Online (Sandbox Code Playgroud)
Ric*_*eck 12
刚刚遇到这个问题Rails 4.2.我找到了两种解决方法:
-
问题是Rails使用base_classSTI关系的名称.
其他答案中记录了其原因,但要点是核心团队似乎觉得您应该能够引用表而不是类来进行多态STI关联.
我不同意这个想法,但我不是Rails核心团队的一员,所以没有太多的意见来解决它.
有两种方法可以解决它:
-
class Association < ActiveRecord::Base
belongs_to :associatiable, polymorphic: true
belongs_to :associated, polymorphic: true
before_validation :set_type
def set_type
self.associated_type = associated.class.name
end
end
Run Code Online (Sandbox Code Playgroud)
这将{x}_type在将数据创建到db之前更改记录.这非常有效,并且仍然保留了关联的多态性.
ActiveRecord方法#app/config/initializers/sti_base.rb
require "active_record"
require "active_record_extension"
ActiveRecord::Base.store_base_sti_class = false
#lib/active_record_extension.rb
module ActiveRecordExtension #-> http://stackoverflow.com/questions/2328984/rails-extending-activerecordbase
extend ActiveSupport::Concern
included do
class_attribute :store_base_sti_class
self.store_base_sti_class = true
end
end
# include the extension
ActiveRecord::Base.send(:include, ActiveRecordExtension)
####
module AddPolymorphic
extend ActiveSupport::Concern
included do #-> http://stackoverflow.com/questions/28214874/overriding-methods-in-an-activesupportconcern-module-which-are-defined-by-a-cl
define_method :replace_keys do |record=nil|
super(record)
owner[reflection.foreign_type] = ActiveRecord::Base.store_base_sti_class ? record.class.base_class.name : record.class.name
end
end
end
ActiveRecord::Associations::BelongsToPolymorphicAssociation.send(:include, AddPolymorphic)
Run Code Online (Sandbox Code Playgroud)
解决问题的更系统的方法是编辑ActiveRecord管理它的核心方法.我在这个gem中使用了引用来找出需要修复/覆盖的元素.
这是未经测试的,仍然需要ActiveRecord核心方法的其他部分的扩展,但似乎适用于我的本地系统.
| 归档时间: |
|
| 查看次数: |
15880 次 |
| 最近记录: |