Active Record Association CollectionProxy 不工作:为什么 @owner.@target.count 在 @owner.@target returns 为空数组时产生结果 1?
Active Record Association CollectionProxy not working: why does @owner.@target.count yield a result of 1 when @owner.@target returns an empty array?
我的 Rails 应用程序中有模型:
Sales_Opportunity has_many Swots。
我正在使用 FactoryGirl 设置它们,运行进行测试以表明当我删除我的 Sales_Opportunity 时,我也会导致相关的 Swots 被删除。出于某种原因,在使用 Byebug 进行调试时,我得到了奇怪的结果 - Sales_Opportunity 和 Swot 记录被正确创建,但是当我 运行 sales_opportunity.swots 它 returns [ ] 而sales_opportunity.swots.count returns 1. 更奇怪的是,完全相同的代码可以很好地与另一个对象关联(timeline_events 与 swot 完全一样,但可以完美地与相同的代码一起工作)。
谁能告诉我我做错了什么?
Sales_Opportunity.rb:
class SalesOpportunity < ActiveRecord::Base
default_scope { order('close_date ASC') }
belongs_to :user
belongs_to :company
has_many :key_contacts
has_many :timeline_events, dependent: :destroy
has_many :swots, dependent: :destroy
end
Swot.rb:
class Swot < ActiveRecord::Base
belongs_to :sales_opportunity
validates :swot_details, presence: true
validates :sales_opportunity_id, presence: true
enum swot_type: [:strength, :weakness, :opportunity, :threat]
enum swot_importance: { minimal: 1, marginal: 2, noteworthy: 3, significant: 4, critical: 5 }
validates :swot_importance, presence: true
end
Swot FactoryGirl 规格:
FactoryGirl.define do
factory :swot do
swot_importance "minimal"
swot_details "Some boring details"
swot_type "threat"
trait :attached do
association :sales_opportunity, :with_user_id
end
end
end
Sales_Opportunity FactoryGirl 规格:
FactoryGirl.define do
sequence(:opportunity_name) { |n| "Sales Oppotunity - #{n}" }
factory :sales_opportunity do
user
opportunity_name {generate(:opportunity_name)}
close_date "2014/12/12"
sale_value 10000
company_id 7
trait :with_user_id do
user_id 6
end
end
end
未通过 Rspec 测试:
describe "when swot's parent sales opportunity is destroyed" do
let(:swot) { FactoryGirl.create(:swot, :attached) }
let(:sales_opportunity) { swot.sales_opportunity }
it "should destroy associated swots" do
dswots = sales_opportunity.swots.to_a
byebug
sales_opportunity.destroy
expect(dswots).not_to be_empty
dswots.each do |dswot|
expect(Swot.where(id: dswot.id)).to be_empty
end
end
end
记录 swot 时控制台(byebug)的输出:
#<Swot id: 13, swot_type: 3, swot_importance: 1, sales_opportunity_id: 564, swot_details: "Some boring details", created_at: "2015-07-27 10:57:23", updated_at: "2015-07-27 10:57:23">
记录时控制台的输出sales_opportunity:
#<SalesOpportunity id: 564, close_date: "2014-12-12 00:00:00", user_id: 6, created_at: "2015-07-27 10:57:23", updated_at: "2015-07-27 10:57:23", pipeline_status: 0, opportunity_name: "Sales Oppotunity - 4", company_id: 7, sale_value: #<BigDecimal:7fe9ffd25078,'0.1E5',9(27)>, swot_score: 0>
sales_opportunity.swots 的输出:
(byebug) sales_opportunity.swots.count
1
sales_opportunity.swots 的输出:
(byebug) sales_opportunity.swots
#<ActiveRecord::Associations::CollectionProxy []>
我想我已经包含了所有已知信息。 Rspec 测试、FactoryGirl 工厂和 sales_opportunities 和 Swots/Timeline_Events 之间的设置完全相同 - 但是 Rspec 测试通过了 Timeline_Events 和 collection_proxy 适用于那些(据我所知,代码是相同的):
Timeline_Event工厂:
FactoryGirl.define do
factory :timeline_event do
activity "Some activity"
due_date "2014/11/11"
trait :attached do
association :sales_opportunity, :with_user_id
end
end
end
工作 Rspec 测试:
describe "when sales opportunity is destroyed for timeline event" do
let(:timeline_event) { FactoryGirl.create(:timeline_event, :attached) }
let(:sales_opportunity) { timeline_event.sales_opportunity }
it "should destroy associated timeline events" do
timeline_events = sales_opportunity.timeline_events.to_a
sales_opportunity.destroy
expect(timeline_events).not_to be_empty
timeline_events.each do |event|
expect(TimelineEvent.where(id: event.id)).to be_empty
end
end
end
Timeline_Event.rb:
class TimelineEvent < ActiveRecord::Base
belongs_to :sales_opportunity
validates :activity, presence: true
validates :due_date, presence: true
validates :sales_opportunity_id, presence: true
end
当 运行ning byebug 在这里的同一个地方时,我得到一个包含 Timeline_Event.
的数组
任何人都可以帮助我了解我的代码中出了什么问题吗?
谢谢。
RSpec.describe SalesOpportunity, type: :model do
let(:sales_opportunity) { create(:sales_opportunity) }
describe "swots" do
let!(:swot) { create(:swot, sales_opportunity: sales_opportunity) }
it "destroys nested swots" do
sales_opportunity.destroy
swot.reload
expect(swot.destroyed?).to be_truthy
end
end
end
请注意,我将其添加到 SalesOpportunity 规范中,因为相关的销毁行为属于 SalesOpportunity 而不是子关联。
编辑。
编写此规范的另一种方法是:
it "destroys nested swots" do
expect {
sales_opportunity.destroy
}.to change(Swot, :count).by(-1)
end
我用这个解决了这个问题 - 似乎需要重新加载 sales_opportunity 才能持久保存 Active Record 关联。 This answer是解题的关键。
这是工作代码:
describe "when swot's parent sales opportunity is destroyed" do
let!(:swot) { FactoryGirl.create(:swot, sales_opportunity: sales_opportunity) }
it "should destroy associated swots" do
sales_opportunity.reload
expect { sales_opportunity.destroy }.to change(sales_opportunity.swots, :count).by(-1)
end
end
使用 Max 上面回答的一些元素也帮助我改进了代码的外观。
我的 Rails 应用程序中有模型:
Sales_Opportunity has_many Swots。
我正在使用 FactoryGirl 设置它们,运行进行测试以表明当我删除我的 Sales_Opportunity 时,我也会导致相关的 Swots 被删除。出于某种原因,在使用 Byebug 进行调试时,我得到了奇怪的结果 - Sales_Opportunity 和 Swot 记录被正确创建,但是当我 运行 sales_opportunity.swots 它 returns [ ] 而sales_opportunity.swots.count returns 1. 更奇怪的是,完全相同的代码可以很好地与另一个对象关联(timeline_events 与 swot 完全一样,但可以完美地与相同的代码一起工作)。
谁能告诉我我做错了什么?
Sales_Opportunity.rb:
class SalesOpportunity < ActiveRecord::Base
default_scope { order('close_date ASC') }
belongs_to :user
belongs_to :company
has_many :key_contacts
has_many :timeline_events, dependent: :destroy
has_many :swots, dependent: :destroy
end
Swot.rb:
class Swot < ActiveRecord::Base
belongs_to :sales_opportunity
validates :swot_details, presence: true
validates :sales_opportunity_id, presence: true
enum swot_type: [:strength, :weakness, :opportunity, :threat]
enum swot_importance: { minimal: 1, marginal: 2, noteworthy: 3, significant: 4, critical: 5 }
validates :swot_importance, presence: true
end
Swot FactoryGirl 规格:
FactoryGirl.define do
factory :swot do
swot_importance "minimal"
swot_details "Some boring details"
swot_type "threat"
trait :attached do
association :sales_opportunity, :with_user_id
end
end
end
Sales_Opportunity FactoryGirl 规格:
FactoryGirl.define do
sequence(:opportunity_name) { |n| "Sales Oppotunity - #{n}" }
factory :sales_opportunity do
user
opportunity_name {generate(:opportunity_name)}
close_date "2014/12/12"
sale_value 10000
company_id 7
trait :with_user_id do
user_id 6
end
end
end
未通过 Rspec 测试:
describe "when swot's parent sales opportunity is destroyed" do
let(:swot) { FactoryGirl.create(:swot, :attached) }
let(:sales_opportunity) { swot.sales_opportunity }
it "should destroy associated swots" do
dswots = sales_opportunity.swots.to_a
byebug
sales_opportunity.destroy
expect(dswots).not_to be_empty
dswots.each do |dswot|
expect(Swot.where(id: dswot.id)).to be_empty
end
end
end
记录 swot 时控制台(byebug)的输出:
#<Swot id: 13, swot_type: 3, swot_importance: 1, sales_opportunity_id: 564, swot_details: "Some boring details", created_at: "2015-07-27 10:57:23", updated_at: "2015-07-27 10:57:23">
记录时控制台的输出sales_opportunity:
#<SalesOpportunity id: 564, close_date: "2014-12-12 00:00:00", user_id: 6, created_at: "2015-07-27 10:57:23", updated_at: "2015-07-27 10:57:23", pipeline_status: 0, opportunity_name: "Sales Oppotunity - 4", company_id: 7, sale_value: #<BigDecimal:7fe9ffd25078,'0.1E5',9(27)>, swot_score: 0>
sales_opportunity.swots 的输出:
(byebug) sales_opportunity.swots.count
1
sales_opportunity.swots 的输出:
(byebug) sales_opportunity.swots
#<ActiveRecord::Associations::CollectionProxy []>
我想我已经包含了所有已知信息。 Rspec 测试、FactoryGirl 工厂和 sales_opportunities 和 Swots/Timeline_Events 之间的设置完全相同 - 但是 Rspec 测试通过了 Timeline_Events 和 collection_proxy 适用于那些(据我所知,代码是相同的):
Timeline_Event工厂:
FactoryGirl.define do
factory :timeline_event do
activity "Some activity"
due_date "2014/11/11"
trait :attached do
association :sales_opportunity, :with_user_id
end
end
end
工作 Rspec 测试:
describe "when sales opportunity is destroyed for timeline event" do
let(:timeline_event) { FactoryGirl.create(:timeline_event, :attached) }
let(:sales_opportunity) { timeline_event.sales_opportunity }
it "should destroy associated timeline events" do
timeline_events = sales_opportunity.timeline_events.to_a
sales_opportunity.destroy
expect(timeline_events).not_to be_empty
timeline_events.each do |event|
expect(TimelineEvent.where(id: event.id)).to be_empty
end
end
end
Timeline_Event.rb:
class TimelineEvent < ActiveRecord::Base
belongs_to :sales_opportunity
validates :activity, presence: true
validates :due_date, presence: true
validates :sales_opportunity_id, presence: true
end
当 运行ning byebug 在这里的同一个地方时,我得到一个包含 Timeline_Event.
的数组任何人都可以帮助我了解我的代码中出了什么问题吗?
谢谢。
RSpec.describe SalesOpportunity, type: :model do
let(:sales_opportunity) { create(:sales_opportunity) }
describe "swots" do
let!(:swot) { create(:swot, sales_opportunity: sales_opportunity) }
it "destroys nested swots" do
sales_opportunity.destroy
swot.reload
expect(swot.destroyed?).to be_truthy
end
end
end
请注意,我将其添加到 SalesOpportunity 规范中,因为相关的销毁行为属于 SalesOpportunity 而不是子关联。
编辑。
编写此规范的另一种方法是:
it "destroys nested swots" do
expect {
sales_opportunity.destroy
}.to change(Swot, :count).by(-1)
end
我用这个解决了这个问题 - 似乎需要重新加载 sales_opportunity 才能持久保存 Active Record 关联。 This answer是解题的关键。
这是工作代码:
describe "when swot's parent sales opportunity is destroyed" do
let!(:swot) { FactoryGirl.create(:swot, sales_opportunity: sales_opportunity) }
it "should destroy associated swots" do
sales_opportunity.reload
expect { sales_opportunity.destroy }.to change(sales_opportunity.swots, :count).by(-1)
end
end
使用 Max 上面回答的一些元素也帮助我改进了代码的外观。