Як створити асоціації has_and_belongs_to_many у Factory girl


119

З огляду на наступне

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
Дякую, найакуратніші з усіх рішень.
Мік

Дякую. Це вирішило мою проблему після годинних розчарувань.
Тоні Бенінат

Це працює для мене лише тоді, коли всі заводи знаходяться в одному файлі, що зовсім небажано. Тому рішення, згадане нижче @opsb, здається, краще.
шпиль

40

З тих пір Factorygirl оновлюється і тепер включає зворотній зв'язок для вирішення цієї проблеми. Погляньте на http://robots.thoughtbot.com/post/254496652/aint-no-calla-back-girl для отримання додаткової інформації.


37
Посилання насправді не говорить про те, як поводитися з has_and_belongs_to_many ... Я не бачу, як це зробити ...
dmonopoly

3
Синтаксис зворотного виклику тепер змінено на: after(:create)замість after_createфабричної дівчини, як згадується тут: stackoverflow.com/questions/15003968/…
Михайло Ягудаєв

22

На мій погляд, просто створіть дві різні фабрики на кшталт:

 Factory.define: user,: class => Користувач do | u |
  # Просто нормальна ініціалізація атрибутів
 кінець

 Factory.define: компанія,: class => Компанія робить | u |
  # Просто нормальна ініціалізація атрибутів
 кінець

Коли ви пишете тестові випадки для користувача, тоді просто пишіть так

 Фабрика (: користувач,: компанії => [Фабрика (: компанія)])

Сподіваюся, це спрацює.


2
Дякую, це єдиний приклад, над яким я міг працювати. Фабрична дівчина - це великий головний біль для habtm.
jspooner

Це більше не працює з останніми версіями FactoryGirl (я думаю, що Rails 3)
Раф

9

Я не міг знайти приклад для вищезгаданого випадку на наданому веб-сайті. (Тільки 1: N та поліморфні асоціації, але не хабтм). У мене був подібний випадок, і мій код виглядає приблизно так:

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

Попередження: Змініть користувачів: [user] на: users => [user] для ruby ​​1.8.x


4
Хіба це не повинно бути: after_create { |company, evaluator| FactoryGirl.create_list(:user, evaluator.users_count, companies: [company]) }?
Раф

0

Перш за все, я наполегливо рекомендую вам використовувати has_many: через замість 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

Після цього у вас буде об'єднання has_many з обох сторін і ви можете призначити їх у factory_girl так, як ви це зробили.


3
Чи не повинно це бути Employment belongs_to :userі Employment belongs_to :companyз моделлю приєднання, що з'єднує одну компанію з одним користувачем?
Даніель Бердслі

5
Мій висновок із швидкого читання публікації, яку ви згадали, полягає в тому, що вибирати habtm чи has_many: through, це залежить від вашого випадку використання. Справжнього «переможця» немає.
auralbee

Ну, єдиний накладний витрата при використанні hmt - це те, що ви повинні мати ідентифікатор, визначений на наскрізній таблиці. Зараз я не можу уявити ситуацію, коли це могло б викликати будь-які проблеми. Я не кажу, що habtm не приносить користі, лише те, що у 99% випадків використання має більше сенсу використовувати hmt (через його переваги).
Мілан Новота

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

Атрибут companies_count є тимчасовим і доступний в атрибутах заводу та у зворотному дзвінку через оцінювач. Тепер ви можете створити користувача з компаніями з можливістю вказати, скільки компаній вам потрібно:

create(:user_with_companies).companies.length # 10
create(:user_with_companies, companies_count: 15).companies.length # 15
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.