我有两个型号。
父模型Tag
:
class Tag < ApplicationRecord
has_many :keywords, inverse_of: :tag, dependent: :destroy
accepts_nested_attributes_for :keywords
validates :keywords, presence: true
end
正如您所看到的,tag
应该至少有一个keyword
。
儿童模型Keyword
:
class Keyword < ApplicationRecord
belongs_to :tag, inverse_of: :keywords
validates :tag, presence: true
end
以下是FactoryGirl
工厂的代码
tag
工厂:
FactoryGirl.define do
factory :tag do
sequence(:name) { |n| "Tag#{n}" }
after(:build) do |tag_object|
tag_object.keywords << build(:keyword, tag: tag_object)
end
end
end
keyword
工厂:
FactoryGirl.define do
factory :keyword do
tag
sequence(:name) { |n| "Keyword#{n}" }
end
end
当我在带有keywords
工厂的keyword
表中创建新记录时,它会在keywords
表中再创建一条记录,该记录与tags
表中的同一父记录相关联
如何省略在keywords
表中再创建一条记录并保持工厂有效?
irb(main):023:0> FactoryGirl.create :keyword
(0.1ms) BEGIN
Keyword Exists (0.7ms) SELECT 1 AS one FROM "keywords" WHERE "keywords"."name" = $1 LIMIT $2 [["name", "Keyword1"], ["LIMIT", 1]]
Tag Exists (0.3ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = $1 LIMIT $2 [["name", "Tag1"], ["LIMIT", 1]]
SQL (0.5ms) INSERT INTO "tags" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["name", "Tag1"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
SQL (0.6ms) INSERT INTO "keywords" ("tag_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["tag_id", 36], ["name", "Keyword1"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
(10.4ms) COMMIT
(0.1ms) BEGIN
Keyword Exists (0.4ms) SELECT 1 AS one FROM "keywords" WHERE "keywords"."name" = $1 LIMIT $2 [["name", "Keyword2"], ["LIMIT", 1]]
SQL (0.4ms) INSERT INTO "keywords" ("tag_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["tag_id", 36], ["name", "Keyword2"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
(4.4ms) COMMIT
=> #<Keyword id: 63, tag_id: 36, name: "Keyword2", created_at: "2017-01-21 19:20:14", updated_at: "2017-01-21 19:20:14">
irb(main):024:0>
您可以看到它在tags
中创建了一条记录,在keywords
表中创建了一条记录,之后又在keywords
表中创建了一条记录。
答案 0 :(得分:1)
FactoryGirl
在构建过程中为模型创建所有声明的关联。这意味着FactoryGirl.build :keyword
会执行FactoryGirl.create :tag
,因此Keyword#tag_id
会有Keyword
的ID来帮助传递irb(main):023:0> FactoryGirl.create :keyword
### keywordA = Keyword.new
### call create(:tag) because of association
### tag1 = Tag.new
### call build(:keyword) in after(:build)
###.keywordB.new(tag: tag1) # which prevents trying to make a new tag!
### tag1.save # which saves the keywordB
(0.1ms) BEGIN
Keyword Exists (0.7ms) SELECT 1 AS one FROM "keywords" WHERE "keywords"."name" = $1 LIMIT $2 [["name", "Keyword1"], ["LIMIT", 1]]
Tag Exists (0.3ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = $1 LIMIT $2 [["name", "Tag1"], ["LIMIT", 1]]
SQL (0.5ms) INSERT INTO "tags" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["name", "Tag1"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
SQL (0.6ms) INSERT INTO "keywords" ("tag_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["tag_id", 36], ["name", "Keyword1"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
(10.4ms) COMMIT
### keywordA.tag = tag1
### keywordA.save
(0.1ms) BEGIN
Keyword Exists (0.4ms) SELECT 1 AS one FROM "keywords" WHERE "keywords"."name" = $1 LIMIT $2 [["name", "Keyword2"], ["LIMIT", 1]]
SQL (0.4ms) INSERT INTO "keywords" ("tag_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["tag_id", 36], ["name", "Keyword2"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
(4.4ms) COMMIT
### Since keywordA gets saved after keywordB,
### keywordB gets a 1 from the sequence and
### keywordA gets a 2 from the sequence
=> #<Keyword id: 63, tag_id: 36, name: "Keyword2", created_at: "2017-01-21 19:20:14", updated_at: "2017-01-21 19:20:14">
irb(main):024:0>
模型上的验证。
这与您看到的数据库活动一致。
keyword
这只是所发生事情的要点。就个人而言,我无法想象基于数据库的架构想要tag
没有它create(:tag)
所以我只需要调用keyword
并获得前面提到的第一个FactoryGirl.define do
factory :tag do
sequence(:name) { |n| "Tag#{n}" }
after(:build) do |this|
this.keywords << build(:keyword) if this.keywords.empty?
end
end
end
FactoryGirl.define do
factory :keyword do
sequence(:name) { |n| "Keyword#{n}" }
after(:build) do |this|
this.tag ||= build(:tag)
end
end
end
build(:tag) # unsaved
build(:tag).keyword # also unsaved
create(:tag) # saved
create(:tag).keyword # also saved
build(:keyword) # unsaved
build(:keyword).tag # also unsaved
create(:keyword) # saved
create(:keyword).tag # also saved
# And it still lets you be specific
create(:tag, keywords: [create(:keyword, name: "More of a phrase")])
create(:keyword, tag: create(:tag, name: "Pop Me!"))
。但是模式很简单,因此在我们想要测试的100%的情况中,以下内容应该成立:
# Fake the association
FactoryGirl.define do
factory :keyword do
sequence(:name) { |n| "Keyword#{n}" }
tag_id 1 # Danger!
# Will make it pass validation but you
# will forget and #tag will not be found
# or not what you expect
end
end
# use a trait
FactoryGirl.define do
factory :keyword do
sequence(:name) { |n| "Keyword#{n}" }
trait :with_tag do
tag
end
end
end
# make a new factory
FactoryGirl.define do
# do not need parent if inside the "factory :tag do"
factory :tag_with_keyword, parent: :tag do
sequence(:name) { |n| "Tag#{n}" }
keyword
end
end
# but now we are back to it creating the keyword on build(:tag)
还需考虑其他几个选项:
FactoryGirl
virtualenv
确实为你提供了解决许多情况的足够选择,但诀窍在于理解它如何设置关联并试图远离设置隐式循环。
答案 1 :(得分:0)
关键字和标签不能彼此独立存在。您的标签工厂每次调用时都会创建一个关键字,因此您应该调用标签工厂。试试这个:
tag = FactoryGirl.create(:tag)
keyword = tag.keywords.first