在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
,因此创建的记录没有设置其他属性。 (注意new
或initialize
只在内存中初始化一个Ruby对象,只有像update
、save
或create
这样的方法才真正写一条记录到数据库。)
与其调用 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
备注:
- 我建议在这里使用
find_or_initialize_by
而不是 find_or_create_by
来跳过额外的数据库查询,因为之后的 update
无论如何都会在下一步中将记录保存到数据库中。
- 如果验证失败,我建议
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?方法。
编辑:
试一试替代解决方案,但使用此数据
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
,因此创建的记录没有设置其他属性。 (注意new
或initialize
只在内存中初始化一个Ruby对象,只有像update
、save
或create
这样的方法才真正写一条记录到数据库。)
与其调用 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
备注:
- 我建议在这里使用
find_or_initialize_by
而不是find_or_create_by
来跳过额外的数据库查询,因为之后的update
无论如何都会在下一步中将记录保存到数据库中。 - 如果验证失败,我建议
update!
instead ofupdate
引发异常(而不是静默失败而不保存)。
替代方法
如果更改现有代码的最小努力很重要,您可以使用更接近原始代码的替代方法:
...
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?方法。