应该 have_many 与 class_name、foreign_key 和 primary_key
Shoulda have_many with class_name, foreign_key and primary_key
如何使用 ShouldaMatchers 测试这个 ActiveRecord 关系?
型号
class ViolatorUnitHistory < ActiveRecord::Base
...
belongs_to :primary_source, class_name: Source, primary_key: :source_1_id, foreign_key: :id
belongs_to :secondary_source, class_name: Source, primary_key: :source_2_id, foreign_key: :id
belongs_to :tertiary_source, class_name: Source, primary_key: :source_3_id, foreign_key: :id
...
end
class Source < ActiveRecord::Base
has_many :violator_unit_histories
end
测试
describe "relationships" do
# Can't figure out this relationship
it { is_expected.to have_many(:violator_unit_histories).class_name('Source').with_primary_key('source_1_id').with_foreign_key('id') }
end
当前结果
Failures:
1) Source relationships should have many violator_unit_histories class_name => Source
Failure/Error: it { is_expected.to have_many(:violator_unit_histories).class_name('Source').with_primary_key('source_1_id').with_foreign_key('id') }
Expected Source to have a has_many association called violator_unit_histories ()
# ./spec/models/source_spec.rb:17:in `block (3 levels) in <top (required)>'
以前的结果
it { is_expected.to have_many(:violator_unit_histories) }
Failures:
1) Source relationships should have many violator_unit_histories
Failure/Error: it { is_expected.to have_many(:violator_unit_histories) }
Expected Source to have a has_many association called violator_unit_histories (ViolatorUnitHistory does not have a source_id foreign key.)
我看到 here 测试 belongs_to
方面的答案。但我似乎无法理解他 has_many
对这些更复杂测试的看法。
首先,您的主键/外键关系看起来有点不对劲。
从您的模型定义开始:
class ViolatorUnitHistory < ActiveRecord::Base
...
belongs_to :primary_source, class_name: Source, primary_key: :source_1_id, foreign_key: :id
belongs_to :secondary_source, class_name: Source, primary_key: :source_2_id, foreign_key: :id
belongs_to :tertiary_source, class_name: Source, primary_key: :source_3_id, foreign_key: :id
...
end
class Source < ActiveRecord::Base
has_many :violator_unit_histories
end
这里的问题是 :id
似乎不是外键,而是主键。相反,source_1_id
似乎是在 ViolatorUnitHistory
模型上定义的外键。这是正确的吗?
换句话说,假设您有一个 Source
的实例与关联的 violator_unit_histories
,您期望 @source.violator_unit_histories
到 return 是什么?您的代码中没有任何内容可以清楚地定义 violator_unit_histories
实际上是什么。
这就是您收到此错误的原因:
ViolatorUnitHistory does not have a source_id foreign key.
Rails 正在 ViolatorUnitHistory
class 中寻找 source_id
外键,但是不存在。
此外,在您的测试中,您没有明确定义测试的 subject
是什么。考虑使用 expect(<object>).to
语法使事情更清楚。
借用 Anthony E 所说的,ViolatorUnitHistory => Source 之间的关系已明确定义,但反之则不然。您可以说 Violator.first.primary_source
,但 Source.first.violator_unit_histories
不正确,因为数据库是如何规范化的。
我可能会以与 ViolatorUnitHistory => Source 关系类似的方式定义每个反向关系,因为 ViolatorUnitHistory 使用多个字段来存储 Source.id
您可以添加如下内容:
class Source < ActiveRecord::Base
...
# I don't think primary_, secondary_ etc sources is a great name for these, since
# since it's really "History that has this as a primary source"
has_many :primary_sources, class_name: ViolatorUnitHistory, primary_key: :id, foreign_key: :source_1_id
has_many :secondary_sources, class_name: ViolatorUnitHistory, primary_key: :id, foreign_key: :source_2_id
has_many :tertiary_sourcs, class_name: ViolatorUnitHistory, primary_key: :id, foreign_key: :source_3_id
...
end
然后你的测试变成这样:
describe "relationships" do
expect(source).to have_many :primary_sources
expect(source).to have_many :secondary_sources
expect(source).to have_many :tertiary_sources
end
另一种方法是规范化您的数据库,这样您就有了一个连接 table,其中包括 ViolatorUnitHistory.id
、Source.id
和一些序数指示符(source_order 或其他东西, 对于你的第一个、第二个、第三个)
披露:我和 Chris 一起工作,我看过实际的数据源,所以我对这个问题有了更多的了解
如何使用 ShouldaMatchers 测试这个 ActiveRecord 关系?
型号
class ViolatorUnitHistory < ActiveRecord::Base
...
belongs_to :primary_source, class_name: Source, primary_key: :source_1_id, foreign_key: :id
belongs_to :secondary_source, class_name: Source, primary_key: :source_2_id, foreign_key: :id
belongs_to :tertiary_source, class_name: Source, primary_key: :source_3_id, foreign_key: :id
...
end
class Source < ActiveRecord::Base
has_many :violator_unit_histories
end
测试
describe "relationships" do
# Can't figure out this relationship
it { is_expected.to have_many(:violator_unit_histories).class_name('Source').with_primary_key('source_1_id').with_foreign_key('id') }
end
当前结果
Failures:
1) Source relationships should have many violator_unit_histories class_name => Source
Failure/Error: it { is_expected.to have_many(:violator_unit_histories).class_name('Source').with_primary_key('source_1_id').with_foreign_key('id') }
Expected Source to have a has_many association called violator_unit_histories ()
# ./spec/models/source_spec.rb:17:in `block (3 levels) in <top (required)>'
以前的结果
it { is_expected.to have_many(:violator_unit_histories) }
Failures:
1) Source relationships should have many violator_unit_histories
Failure/Error: it { is_expected.to have_many(:violator_unit_histories) }
Expected Source to have a has_many association called violator_unit_histories (ViolatorUnitHistory does not have a source_id foreign key.)
我看到 here 测试 belongs_to
方面的答案。但我似乎无法理解他 has_many
对这些更复杂测试的看法。
首先,您的主键/外键关系看起来有点不对劲。
从您的模型定义开始:
class ViolatorUnitHistory < ActiveRecord::Base
...
belongs_to :primary_source, class_name: Source, primary_key: :source_1_id, foreign_key: :id
belongs_to :secondary_source, class_name: Source, primary_key: :source_2_id, foreign_key: :id
belongs_to :tertiary_source, class_name: Source, primary_key: :source_3_id, foreign_key: :id
...
end
class Source < ActiveRecord::Base
has_many :violator_unit_histories
end
这里的问题是 :id
似乎不是外键,而是主键。相反,source_1_id
似乎是在 ViolatorUnitHistory
模型上定义的外键。这是正确的吗?
换句话说,假设您有一个 Source
的实例与关联的 violator_unit_histories
,您期望 @source.violator_unit_histories
到 return 是什么?您的代码中没有任何内容可以清楚地定义 violator_unit_histories
实际上是什么。
这就是您收到此错误的原因:
ViolatorUnitHistory does not have a source_id foreign key.
Rails 正在 ViolatorUnitHistory
class 中寻找 source_id
外键,但是不存在。
此外,在您的测试中,您没有明确定义测试的 subject
是什么。考虑使用 expect(<object>).to
语法使事情更清楚。
借用 Anthony E 所说的,ViolatorUnitHistory => Source 之间的关系已明确定义,但反之则不然。您可以说 Violator.first.primary_source
,但 Source.first.violator_unit_histories
不正确,因为数据库是如何规范化的。
我可能会以与 ViolatorUnitHistory => Source 关系类似的方式定义每个反向关系,因为 ViolatorUnitHistory 使用多个字段来存储 Source.id
您可以添加如下内容:
class Source < ActiveRecord::Base
...
# I don't think primary_, secondary_ etc sources is a great name for these, since
# since it's really "History that has this as a primary source"
has_many :primary_sources, class_name: ViolatorUnitHistory, primary_key: :id, foreign_key: :source_1_id
has_many :secondary_sources, class_name: ViolatorUnitHistory, primary_key: :id, foreign_key: :source_2_id
has_many :tertiary_sourcs, class_name: ViolatorUnitHistory, primary_key: :id, foreign_key: :source_3_id
...
end
然后你的测试变成这样:
describe "relationships" do
expect(source).to have_many :primary_sources
expect(source).to have_many :secondary_sources
expect(source).to have_many :tertiary_sources
end
另一种方法是规范化您的数据库,这样您就有了一个连接 table,其中包括 ViolatorUnitHistory.id
、Source.id
和一些序数指示符(source_order 或其他东西, 对于你的第一个、第二个、第三个)
披露:我和 Chris 一起工作,我看过实际的数据源,所以我对这个问题有了更多的了解