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 上面回答的一些元素也帮助我改进了代码的外观。