在Rails 5、如何让Rails将我的所有属性保存在一个种子文件中定义的对象中?

In Rails 5, how do I make Rails save all my attributes in an object defined in a seed file?

编辑:

试一试替代解决方案,但使用此数据

crawlers = []
crawler1 = WebCrawler.new({ 
  class_name: "MyObjectService",
  start_url: "stuff?format=json&key=88b3284d4b892b430767b830d0c0eb51", 
  begin_with_start_url: false, 
  active: true
})
crawlers.push(crawler1)

调用 rake db:seed 失败并显示以下消息。显然,即使该行已经存在,Rails 仍试图用空值更新其 id 列。

rake aborted!
ActiveRecord::StatementInvalid: PG::NotNullViolation: ERROR:  null value in column "id" violates not-null constraint
DETAIL:  Failing row contains (null, MyObjectService, null, null, stuff/500?format=json&key=88b3284d4b892..., null, f, t, null, null, null, 1).
: UPDATE "web_crawlers" SET "id" = , "start_time" = , "end_time" = , "active" = , "created_at" = , "updated_at" =  WHERE "web_crawlers"."id" = 
/Users/davea/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:598:in `async_exec'
/Users/davea/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/postgresql_adapter.rb:598:in `block in exec_no_cache'
/Users/davea/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log'

编辑:

这种方法不需要在很多地方更新代码。我们使用 .attributes 来获取在 .new 调用中分配的属性集。然后我们检查以找到一个 WebCrawler 实例,其中定义了每个爬虫实例的 class_name。我们从那里更新或创建。

crawlers = []

crawler7 = WebCrawler.new({
  class_name: "MainService",
  start_url: "...",
  ...
})

crawlers.push(crawler7)

crawlers.each do |c|
  web_crawler = WebCrawler.find_by(class_name: c.attributes['class_name'])

  if web_crawler
    web_crawler.update(c.attributes)
  else
    c.save
  end
end

Here's a link to the docs.见header下的第一句:"Finds the first record with the given attributes, or creates a record with the attributes if one is not found."由于给定的属性只是名称,它违反了您的验证。

请参阅 find_or_create_by 的文档:

Finds the first record with the given attributes, or creates a record with the attributes if one is not found

由于您只将 class_name 属性传递给 find_or_create_by,因此创建的记录没有设置其他属性。 (注意newinitialize只在内存中初始化一个Ruby对象,只有像updatesavecreate这样的方法才真正写一条记录到数据库。)

与其调用 WebCrawler.new 然后将这些对象的属性传递给单独的 find_or_create_by 调用,更简单的方法是从一开始就调用 finder 方法,更新 newly-created 或带有附加参数的现有记录:

...
crawler7 = WebCrawler.find_or_initialize_by( 
  class_name: "MainService"
).update!(
  start_url: "...", 
  ...
)
#  Done- the record is created/updated in the database

备注:

  1. 我建议在这里使用 find_or_initialize_by 而不是 find_or_create_by 来跳过额外的数据库查询,因为之后的 update 无论如何都会在下一步中将记录保存到数据库中。
  2. 如果验证失败,我建议 update! instead of update 引发异常(而不是静默失败而不保存)。

替代方法

如果更改现有代码的最小努力很重要,您可以使用更接近原始代码的替代方法:

...
crawler7 = WebCrawler.new({ 
  class_name: "MainService",
  start_url: "...", 
  ...
})
crawlers.push(crawler7)
#  

# Create the crawlers 
crawlers.each do |c|
  crawler = WebCrawler.find_or_initialize_by(class_name: c.class_name)
  crawler.update!(c.attributes)
end

这种方法不太简单(因为它遍历所有对象两次)并且效率较低(因为它为每条记录初始化 2 Ruby 个对象),但它仍然可以完成工作。

Model#new 仅实例化一个对象,但不会将其保存在数据库中,您仍然需要在实例上调用 Model#save。

Model#create 立即在数据库中创建它,假设它通过了验证。这应该可以解决您在错误消息中遇到的空问题。本质上,您正在尝试更新尚未持久化的对象。您可以通过 persisted?方法。