factory_girl / factory_bot深层嵌套关联没有后(:create)

时间:2017-11-30 22:52:24

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

型号:

class User < ApplicationRecord
  has_many :blogs
end

class Blog < ApplicationRecord
  belongs_to :user
  has_many :posts

  validates_presence_of :user_id
  # a blog record cannot be created without at least one associated post record
  validates :posts, :length => { :minimum => 1 }
end

class Post < ApplicationRecord
  belongs_to :blog
  belongs_to :user

  validates_presence_of :user_id, :blog_id
end

验证是我工厂难以实现的原因。请注意,除非blog至少有一个post,否则无法创建post。另请注意,除非blog_idblog,否则无法创建post。换句话说:application.rbbelongs_to需要构建,它们需要相互关联,并且需要保存它们同时使验证通过。

这是使用Rails 5,所以我确实对# config/application.rb module MyApp class Application < Rails::Application Rails.application.config.active_record.belongs_to_required_by_default = false end end 进行了调整,以便FactoryGirl.define do factory :user do end end FactoryGirl.define do factory :blog do user factory :blog_with_post do after(:create) do |blog, eval| the_user_of_blog = blog.user create(:post, blog: blog, user: the_user_of_blog) end end end end FactoryGirl.define do factory :post do blog user end end 关联不会对我的工厂造成太大的麻烦:

user

工厂:

blog

我在测试中所做的是创建一个post记录,然后创建一个user记录和一个@user = create(:user) create(:blog_with_post, user: @user) # => ActiveRecord::RecordInvalid: Validation failed: User can't be blank, Posts is too short (minimum is 1 character) 记录,这两个记录都与同一个{{1}相关联}}。

使用上面的代码:这有效:

after(:build)

尝试

我尝试了factory :blog_with_post do after(:build) do |blog, eval| blog_user = blog.user create(:post, blog: blog, user: blog_user) end end # @user = create(:user) # create(:blog_with_post, user: @user) # => ActiveRecord::RecordInvalid: Validation failed: Blog can't be blank

before(:create)

我也尝试了factory :blog_with_post do before(:create) do |blog, eval| blog_user = blog.user create(:post, blog: blog, user: blog_user) end end # @user = create(:user) # create(:blog_with_post, user: @user) # => ActiveRecord::RecordInvalid: Validation failed: Blog can't be blank 导致同样的错误:

factory :blog_with_post do
  after(:build) do |blog, eval|
    blog_user = blog.user
    build(:post, blog: blog, user: blog_user)
  end
end

# @user = create(:user)
#         create(:blog_with_post, user: @user)
# => ActiveRecord::RecordInvalid: Validation failed: Posts is too short (minimum is 1 character)

我也试过这个:

FactoryGirl.define do
  factory :blog do
    user 

    factory :blog_with_post do
      posts {build_list :post, 1, user: THE_USER_OF_THIS_BLOG}
    end
  end
end

以下内容似乎非常接近,但我不知道如何引用与此博客相关联的用户:

read

1 个答案:

答案 0 :(得分:1)

这与我的结果非常接近,并且排除了在没有至少一个blog的情况下无法创建post的验证。我无法理解这一点(有工厂,我可以做直线轨道)。但是,我确实知道如何让before(:create)工作,而不是使用after(:create)

归根结底:它归结为一种误解,即协会在被拯救之前如何相互了解,以及协会如何在同一时间自然地保存。

detailed association reference rails文档帮助了我很多,结合使用关联构建记录,保存其中一条记录,然后观察由于belongs_to而同时保存它们的方式或模型上指定的has_many关联。

有了这些知识:Post模型上的这两个验证是导致大多数问题的原因:

validates_presence_of :user_id, :blog_id

此问题(关于此问题中的代码)是,当构建但尚未保存时,blog_id将不存在>,所以在某些情况下,工厂无效。

因此我们 。相反,我们希望验证 关联 是否存在。换句话说:它只是滥用presence validation的关联。所以将验证更改为:

blog_id

现在剩下要做的就是以正确的方式写工厂:

validates_presence_of :user, :blog

<强>用法

factory :blog_with_post do
  before(:create) do |blog, eval|
    create(:post, blog: blog, user: blog.user)
  end
end

由此得出:@user = create(:user) create(:blog_with_post, user: @user) blog都与user相关联,postuser相关联,所有都是同时保存。