Як створити асоціації 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.