วิธีสร้าง has_and_belongs_to_many สมาคมใน Factory girl


120

ให้ดังต่อไปนี้

class User < ActiveRecord::Base
  has_and_belongs_to_many :companies
end

class Company < ActiveRecord::Base
  has_and_belongs_to_many :users
end

คุณกำหนดโรงงานสำหรับ บริษัท และผู้ใช้รวมถึงสมาคมแบบสองทิศทางได้อย่างไร นี่คือความพยายามของฉัน

Factory.define :company do |f|
  f.users{ |users| [users.association :company]}
end

Factory.define :user do |f|
  f.companies{ |companies| [companies.association :user]}
end

ตอนนี้ฉันพยายาม

Factory :user

บางทีอาจจะไม่น่าแปลกใจที่สิ่งนี้ส่งผลให้เกิดการวนซ้ำที่ไม่มีที่สิ้นสุดเนื่องจากโรงงานต่างๆใช้กันและกันเพื่อกำหนดตัวเอง

น่าแปลกใจที่ฉันไม่พบการกล่าวถึงวิธีการทำเช่นนี้จากที่ใดมีรูปแบบในการกำหนดโรงงานที่จำเป็นหรือฉันกำลังทำอะไรผิดพลาดโดยพื้นฐานหรือไม่?

คำตอบ:


132

นี่คือวิธีแก้ปัญหาที่เหมาะกับฉัน

FactoryGirl.define do

  factory :company do
    #company attributes
  end

  factory :user do
   companies {[FactoryGirl.create(:company)]}
   #user attributes
  end

end

หากคุณต้องการ บริษัท เฉพาะคุณสามารถใช้โรงงานด้วยวิธีนี้

company = FactoryGirl.create(:company, #{company attributes})
user = FactoryGirl.create(:user, :companies => [company])

หวังว่าจะเป็นประโยชน์สำหรับใครบางคน


4
ขอบคุณมากที่สุดในการแก้ปัญหาทั้งหมด
Mik

ขอบคุณ. สิ่งนี้ได้แก้ไขปัญหาของฉันหลังจากหลายชั่วโมงแห่งความหงุดหงิด
Tony Beninate

สิ่งนี้ใช้ได้เฉพาะกับฉันเมื่อโรงงานทั้งหมดอยู่ในไฟล์เดียวซึ่งค่อนข้างไม่เป็นที่ต้องการ ดังนั้นวิธีแก้ปัญหาที่กล่าวถึงโดย @opsb ด้านล่างน่าจะดีกว่า
spier

40

Factorygirl ได้รับการอัปเดตและตอนนี้มีการโทรกลับเพื่อแก้ปัญหานี้ ดูข้อมูลเพิ่มเติมได้ที่http://robots.thoughtbot.com/post/254496652/aint-no-calla-backgirl


37
ลิงค์ไม่ได้บอกวิธีจัดการ has_and_belongs_to_many จริงๆ ... ฉันไม่เห็นวิธีการทำเช่นนี้ ...
dmonopoly

3
ตอนนี้ไวยากรณ์การโทรกลับถูกเปลี่ยนเป็น: after(:create)แทนที่จะเป็นafter_createในสาวโรงงานตามที่กล่าวไว้ที่นี่: stackoverflow.com/questions/15003968/…
Michael Yagudaev

22

ในความคิดของฉันแค่สร้างโรงงานสองแห่งที่แตกต่างกันเช่น:

 Factory.define: user,: class => User do | u |
  # เพียงการเริ่มต้นคุณสมบัติปกติ
 ปลาย

 Factory.define: บริษัท ,: class => บริษัท ทำ | u |
  # เพียงการเริ่มต้นคุณสมบัติปกติ
 ปลาย

เมื่อคุณเขียนกรณีทดสอบสำหรับผู้ใช้ให้เขียนแบบนี้

 โรงงาน (: ผู้ใช้: บริษัท => [โรงงาน (: บริษัท )])

หวังว่ามันจะได้ผล


2
ขอบคุณนี่เป็นเพียงตัวอย่างเดียวที่ฉันสามารถใช้งานได้ สาวโรงงานปวดหัวหนักเรื่อง habtm
jspooner

สิ่งนี้ใช้ไม่ได้กับ FactoryGirl เวอร์ชันล่าสุดอีกต่อไป (ฉันกำลังคิดว่า Rails 3)
Raf

9

ฉันไม่พบตัวอย่างสำหรับกรณีที่กล่าวถึงข้างต้นในเว็บไซต์ที่ให้มา (เฉพาะ 1: N และการเชื่อมโยงหลายรูปแบบ แต่ไม่มี habtm) ฉันมีกรณีที่คล้ายกันและรหัสของฉันมีลักษณะดังนี้:

Factory.define :user do |user|
 user.name "Foo Bar"
 user.after_create { |u| Factory(:company, :users => [u]) }
end

Factory.define :company do |c|
 c.name "Acme"
end

3
จะเกิดอะไรขึ้นหากมีการตรวจสอบความถูกต้องของจำนวนผู้ใช้ที่ไม่ใช่ศูนย์
dfens

5

สิ่งที่ได้ผลสำหรับฉันคือการตั้งค่าการเชื่อมโยงเมื่อใช้โรงงาน โดยใช้ตัวอย่างของคุณ:

user = Factory(:user)
company = Factory(:company)

company.users << user 
company.save! 

4

พบวิธีนี้ดีและละเอียด:

FactoryGirl.define do
  factory :foo do
    name "Foo" 
  end

  factory :bar do
    name "Bar"
    foos { |a| [a.association(:foo)] }
  end
end

1
foos { |a| [a.association(:foo)] }ช่วยฉันได้มาก! ขอบคุณ!
monteirobrena

3
  factory :company_with_users, parent: :company do

    ignore do
      users_count 20
    end

    after_create do |company, evaluator|
      FactoryGirl.create_list(:user, evaluator.users_count, users: [user])
    end

  end

คำเตือน:เปลี่ยนผู้ใช้: [ผู้ใช้] เป็น: ผู้ใช้ => [ผู้ใช้] สำหรับทับทิม 1.8.x


4
ไม่ควร: after_create { |company, evaluator| FactoryGirl.create_list(:user, evaluator.users_count, companies: [company]) }?
Raf

0

ก่อนอื่นฉันขอแนะนำให้คุณใช้ has_many: through แทน habtm (เพิ่มเติมเกี่ยวกับเรื่องนี้ที่นี่ ) ดังนั้นคุณจะได้รับสิ่งต่อไปนี้:

Employment belongs_to :users
Employment belongs_to :companies

User has_many :employments
User has_many :companies, :through => :employments 

Company has_many :employments
Company has_many :users, :through => :employments

หลังจากนี้คุณจะมีการเชื่อมโยงมากมายทั้งสองด้านและสามารถกำหนดให้กับพวกเขาใน factory_girl ในแบบที่คุณทำ


3
ไม่ควรเป็นเช่นนั้นEmployment belongs_to :userและEmployment belongs_to :companyด้วยรูปแบบการเข้าร่วมที่เชื่อมต่อ บริษัท หนึ่งกับผู้ใช้รายเดียวหรือไม่?
Daniel Beardsley

5
ข้อสรุปของฉันจากการอ่านอย่างรวดเร็วในโพสต์ที่คุณกล่าวถึงคือขึ้นอยู่กับกรณีการใช้งานของคุณว่าจะเลือก habtm หรือ has_many: through ไม่มี "ผู้ชนะ" ที่แท้จริง
auralbee

ค่าใช้จ่ายเพียงอย่างเดียวเมื่อใช้ hmt คือคุณต้องกำหนด id บนตารางผ่าน ตอนนี้ฉันนึกภาพไม่ออกว่าจะเกิดปัญหาอะไรขึ้น ฉันไม่ได้บอกว่า habtm ไม่มีประโยชน์ แต่ใน 99% กรณีการใช้งานมันสมเหตุสมผลกว่าที่จะใช้ hmt (เพราะข้อดีของมัน)
Milan Novota

6
-1 เพียงเพราะ HMT มี 'ข้อดี' มากกว่าหมายความว่าคุณควรใช้หากคุณต้องการข้อดีเหล่านั้น Pet peeve เพราะตอนนี้ฉันกำลังทำโปรเจ็กต์ที่นักพัฒนาใช้ HMT ในหลาย ๆ กรณีที่ HABTM น่าจะเพียงพอ โค้ดเบสจึงมีขนาดใหญ่ขึ้นซับซ้อนขึ้นใช้งานง่ายน้อยลงและสร้างการรวม SQL ที่ช้าลงด้วยเหตุนี้ ดังนั้นให้ใช้ HABTM เมื่อคุณทำได้จากนั้นเมื่อคุณต้องการสร้างโมเดลการเข้าร่วมแยกต่างหากเพื่อจัดเก็บข้อมูลเพิ่มเติมเกี่ยวกับการเชื่อมโยงแต่ละครั้งจากนั้นใช้ HMT
sbeam

0

อัปเดตสำหรับ Rails 5:

แทนที่จะใช้การhas_and_belongs_to_manyเชื่อมโยงคุณควรพิจารณา: การhas_many :throughเชื่อมโยง

โรงงานผู้ใช้สำหรับการเชื่อมโยงนี้มีลักษณะดังนี้:

FactoryBot.define do
  factory :user do
    # user attributes

    factory :user_with_companies do
      transient do
        companies_count 10 # default number
      end

      after(:create) do |user, evaluator|
         create_list(:companies, evaluator.companies_count, user: user)
      end
    end
  end
end

คุณสามารถสร้างโรงงานของ บริษัท ได้ในลักษณะเดียวกัน

เมื่อตั้งโรงงานทั้งสองแล้วคุณสามารถสร้างuser_with_companiesโรงงานด้วยcompanies_count option. คุณสามารถระบุจำนวน บริษัท ที่ผู้ใช้เป็นสมาชิกได้ที่นี่:create(:user_with_companies, companies_count: 15)

คุณสามารถหาคำอธิบายรายละเอียดเกี่ยวกับความสัมพันธ์ของสาวโรงงานที่นี่


0

สำหรับ HABTM ผมใช้ลักษณะและเรียกกลับ

สมมติว่าคุณมีโมเดลต่อไปนี้:

class Catalog < ApplicationRecord
  has_and_belongs_to_many :courses
  
end
class Course < ApplicationRecord
  
end

คุณสามารถกำหนดโรงงานด้านบน :

FactoryBot.define do
  factory :catalog do
    description "Catalog description"
    

    trait :with_courses do
      after :create do |catalog|
        courses = FactoryBot.create_list :course, 2

        catalog.courses << courses
        catalog.save
      end
    end
  end
end

-1

คุณสามารถกำหนดโรงงานใหม่และใช้การเรียกกลับหลัง (: create) เพื่อสร้างรายการการเชื่อมโยง มาดูวิธีทำในตัวอย่างนี้:

FactoryBot.define do

  # user factory without associated companies
  factory :user do
    # user attributes

    factory :user_with_companies do
      transient do
        companies_count 10
      end

      after(:create) do |user, evaluator|
        create_list(:companies, evaluator.companies_count, user: user)
      end
    end
  end
end

แอตทริบิวต์ company_count เป็นคุณสมบัติชั่วคราวและมีอยู่ในแอตทริบิวต์ของโรงงานและในการติดต่อกลับผ่านผู้ประเมิน ตอนนี้คุณสามารถสร้างผู้ใช้กับ บริษัท ต่างๆด้วยตัวเลือกในการระบุจำนวน บริษัท ที่คุณต้องการ:

create(:user_with_companies).companies.length # 10
create(:user_with_companies, companies_count: 15).companies.length # 15
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.