【问题标题】:How do I add extra data to join table in factory_bot factories如何添加额外数据以加入 factory_bot 工厂中的表
【发布时间】:2018-09-22 13:57:01
【问题描述】:

我有用户和站点的模型,它们之间的关系具有 has_many。在sites_users 连接表is_default 上有一个额外的数据,它是布尔类型的,目的是允许每个用户从该用户的相关站点列表中拥有一个默认站点。

用户模型

class User < ApplicationRecord
  has_many :sites_users
  has_many :sites, through: :sites_users
  accepts_nested_attributes_for :sites_users, allow_destroy: true
  ...
end

用户工厂

factory :user do
  sequence(:email) { |n| "user_#{n}@example.com" }
  role { Role.find_by(title: 'Marketing') }
  image { Rack::Test::UploadedFile.new(Rails.root.join('spec', 'support', 'fixtures', 'user.jpg'), 'image/jpeg') }

  factory :super_admin do
    role { Role.find_by(title: "Super Admin") }
    admin true
  end

  before :create do |u|
    u.sites_users.build(site: site, is_default: true)
  end

结束

备用用户工厂方法

在用户工厂中,我也尝试过下面包含的这种方法,但找不到使用这种语法包含is_default: true 的方法。所以我最终放弃了这种方法,转而使用上面的before_create 调用。

factory :user do
  ...
  site { site }
  ...
end

我非常感谢任何人可以提供的任何帮助。谢谢!

架构信息

表格:用户

t.string "email", default: "", null: false
t.boolean "admin", default: false
t.integer "role_id"
t.string "first_name"
t.string "last_name"

表格: 网站

t.string "domain", default: "", null: false
t.string "name", default: "", null: false
t.string "logo"
t.string "logo_mark"

表格: sites_users

t.bigint "site_id", null: false
t.bigint "user_id", null: false
t.boolean "is_default", default: false

【问题讨论】:

  • 您能否在上面的帖子中也包含您的站点和sites_users 工厂?您是否也尝试过使用 after create 而不是 before create?

标签: ruby-on-rails testing ruby-on-rails-5 factory-bot


【解决方案1】:

为 :site_user 创建工厂

factory :site_user, class: SiteUser do 
  site { site } # you could delete this line and add the site in factory :user
  is_default { false } # as specified in your DB
end

不要在:user 工厂中创建site,而是使用漂亮的语法创建它的关系:

factory :user do 
  ...
  sites_users { [FactoryBot.build(:site_user, is_default: true)] }
  ...
end

它应该可以解决问题!

【讨论】:

  • 这行得通,我只需要将site { site } 替换为site。原始抛出:堆栈太深错误。谢谢!
【解决方案2】:

因此,当我处理联接表上的额外字段时,我会创建自定义方法来构建这些联接关系,而不是尝试依赖内置方法的 rails。我已经测试过这个方法,它适用于 factory_bot 就好了。


用户模型

class User < ApplicationRecord
  ...

  def set_site(site, default = false)
    SiteUser.find_or_create_by(
      user_id: id,
      site_id: site.id
    ).update_attribute(:is_default, default)
  end

end

注意:此代码是我使用的一个块,因此我可以通过相同的方法创建新的站点关系并更新默认值。如果需要,您可以将其简化为只创建而不检查是否存在。

用户工厂

factory :user do
  ...

  # to relate to new site with default true
  after(:create) do |u|
    u.set_site(create(:site), true)
  end

  # to relate to existing site with default true
  after(:create) do |u|
    u.set_site(site_name, true)
  end
end

如果这有帮助,请告诉我! (或者如果有人有更默认的 Railsish 方式也可以,我很想听听!)

【讨论】:

    【解决方案3】:

    您可能需要考虑通过更改架构来解决此问题。您可以在 users 表中添加 default_site_id 列,并将默认站点作为用户模型的单独关联进行管理。

    在迁移中:

    add_foreign_key :users, :sites, column: :default_site_id
    

    在用户类中:

    class User < ApplicationRecord
      ...
      belongs_to :default_site, class_name: 'Site'
      ...
    
      # validate that the default site has an association to this user
      validate :default_site_id, inclusion: {in: sites.map(&:id)}, if: Proc.new {default_site_id.present?}
    end
    

    这将简化关联并保证没有用户将拥有多个 is_default 为 true 的 site_users 记录。在工厂中设置默认站点应该很简单。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-14
      • 1970-01-01
      • 2012-10-25
      • 2021-07-20
      • 1970-01-01
      • 2021-08-15
      • 2020-03-24
      • 1970-01-01
      相关资源
      最近更新 更多