如何在factory_bot工厂中的连接表(join table)中添加额外的数据?

5

我有关于用户和站点的模型,它们之间存在一个通过has_many through关系的联系。在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"

table: sites_users

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

你能否在上面的帖子中包含你的sites和sites_users工厂?你是否尝试过使用after create而不是before create? - Chirag
3个回答

4
创建一个 :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

这应该能起作用!


2
这个可以工作了,我只需要用 site 替换 site { site }。原来会抛出“堆栈太深”的错误。谢谢! - Thomas

0

所以当我处理连接表上有额外字段的情况时,我会创建自定义方法来构建这些连接关系,而不是试图依赖于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

请告诉我这是否有帮助!(或者如果有更符合 Rails 默认风格的方法同样有效,我很乐意听取!)


0

你可能需要考虑通过模式更改来解决这个问题。你可以在用户表中添加一个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

这将简化关联并确保没有用户会有多个 site_users 记录,其中 is_default 为 true。在工厂中设置默认站点应该是微不足道的。


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