Factory Girl: 如何设置has_many/through关联

32

我一直在努力使用Factory Girl建立has_many/through关系。

我有以下模型:

class Job < ActiveRecord::Base
  has_many :job_details, :dependent => :destroy
  has_many :details, :through => :job_details
end

class Detail < ActiveRecord::Base
  has_many :job_details, :dependent => :destroy
  has_many :jobs, :through => :job_details
end

class JobDetail < ActiveRecord::Base
  attr_accessible :job_id, :detail_id
  belongs_to :job
  belongs_to :detail
end

我的工厂:

factory :job do
  association     :tenant
  title           { Faker::Company.catch_phrase }
  company         { Faker::Company.name }
  company_url     { Faker::Internet.domain_name }
  purchaser_email { Faker::Internet.email }
  description     { Faker::Lorem.paragraphs(3) }
  how_to_apply    { Faker::Lorem.sentence }
  location        "New York, NY"
end

factory :detail do
  association :detail_type <--another Factory not show here
  description "Full Time"
end

factory :job_detail do
  association :job
  association :detail
end

我希望我的工作工厂创建时默认的Detail为"全职"。

我一直在尝试遵循这个链接,但没有成功:FactoryGirl Has Many through

我不确定如何使用after_create将Detail通过JobDetail附加。

6个回答

36

尝试像这样做。您想要构建一个detail对象并将其附加到作业的详细信息关联。当您使用after_create时,创建的作业将被提供给块。因此,您可以使用FactoryGirl创建一个详细对象,并直接将其添加到该作业的详细信息中。

factory :job do
  ...

  after_create do |job|
    job.details << FactoryGirl.create(:detail)
  end
end

这个很好用,谢谢。有一个问题 - 添加after_create可以工作,但是它会响应DEPRECATION WARNING: You're trying to create an attribute detail_id'. Writing arbitrary attributes on a model is deprecated. Please just use attr_writer etc.` 你有什么想法吗? - cman77
14
我知道这很旧了,但是在 FactoryGirl 中,现在使用格式为 after(:create) 的回调函数,而不是 after_create。答案的其余部分应仍然可以正常工作,没有错误。 - Arel
有关after(:create)回调的更多信息:http://robots.thoughtbot.com/get-your-callbacks-on-with-factory-girl-3-3 - Brian
4
好的,这对于创建是可行的,因为有JobDetail的ID可以引用,但after(:build)呢?有没有办法为未保存的对象建立关联?还是我应该放弃以这种方式工作的想法? - Epigene

4
我今天遇到了这个问题,但我找到了解决方案。希望能帮助到他人。
FactoryGirl.define do
  factory :job do

    transient do
      details_count 5 # if details count is not given while creating job, 5 is taken as default count
    end

    factory :job_with_details do
      after(:create) do |job, evaluator|
        (0...evaluator.details_count).each do |i|
          job.details << FactoryGirl.create(:detail)
        end
      end
    end
  end  
end

这样可以创建一个类似于这样的工作。
create(:job_with_details) #job created with 5 detail objects
create(:job_with_details, details_count: 3) # job created with 3 detail objects

目前能够很好地与最新的一切兼容(Rails 5,RSpec 3.5,FactoryGirl 4.8)。 - jpw

2
这对我有帮助。
FactoryGirl.define do
  factory :job do

    # ... Do whatever with the job attributes here

    factory :job_with_detail do

      # In later (as of this writing, unreleased) versions of FactoryGirl
      # you will need to use `transitive` instead of `ignore` here
      ignore do
        detail { create :detail }
      end

      after :create do |job, evaluator|
        job.details << evaluator.detail
        job.save
        job_detail = job.job_details.where(detail:evaluator.detail).first

        # ... do anything with the JobDetail here

        job_detail.save
      end
    end
  end
end

稍后再做。
# A Detail object is created automatically and associated with the new Job.
FactoryGirl.create :job_with_detail

# To supply a detail object to be associated with the new Job.
FactoryGirl.create :job_with_detail detail:@detail

我知道这个问题很老,但是我很好奇,这个方案比这里的被接受答案更好在哪里? - rorykoehler
当我阅读原始答案时,示例中的信息不足以满足我的需求。这还在原有答案的基础上添加了额外的功能,因为您可以使用带有或不带有detail:@detail选项的create:job_with_detail,如果未提供,则细节将自动创建。 - Nate

1

自FactoryBot v5以来,关联保留了构建策略。 关联是解决这个问题的最佳方法,而文档中有很好的示例

FactoryBot.define :job do
  job_details { [association(:job_detail)] }
end

FactoryBot.define :detail do
  description "Full Time"
end

FactoryBot.define :job_detail do
  association :job
  association :detail
end

0
您可以通过以下方式解决此问题:
FactoryBot.define do
  factory :job do
    # job attributes

    factory :job_with_details do
      transient do
        details_count 10 # default number
      end

      after(:create) do |job, evaluator|
        create_list(:details, evaluator.details_count, job: job)
      end
    end
  end
end

通过这个,你可以创建一个带有详细信息的工作,它有选项来指定你想要多少详细信息。 你可以阅读这篇有趣的文章以获取更多细节。


0

使用当前的factory_bot(之前是factory_girl)实现,gem会处理所有事情,您不需要在jobs.details中创建并推送记录。您只需要这样做

factory :job do
  ...

  factory :job_with_details do
    transient do
      details_count { 5 }
    end

    after(:create) do |job, evaluator|
      create_list(:detail, evaluator.details_count, jobs: [job])
      job.reload
    end
  end  
end

以下代码将生成5个详细作业。
 create(:job_with_details)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接