如何编写 Rake 任务以将数据导入 Rails 应用程序?

Posted

技术标签:

【中文标题】如何编写 Rake 任务以将数据导入 Rails 应用程序?【英文标题】:How to write Rake task to import data to Rails app? 【发布时间】:2011-03-21 17:45:59 【问题描述】:

目标:使用 CRON 任务(或其他预定事件)更新数据库,每晚从现有系统导出数据。

所有数据都在现有系统中创建/更新/删除。该网站不直接与该系统集成,因此 rails 应用程序只需要反映数据导出中出现的更新。

我有一个包含约 5,000 种产品的 .txt 文件,如下所示:

"1234":"product name":"attr 1":"attr 2":"ABC Manufacturing":"2222"
"A134":"another product":"attr 1":"attr 2":"Foobar World":"2447"
...

所有值都是用冒号 (:) 分隔的双引号 (") 括起来的字符串

字段是:

id:唯一标识;字母数字 name:产品名称;任何字符 属性列:字符串;任何字符(例如,大小、重量、颜色、尺寸) vendor_name:字符串;任何字符 vendor_id:唯一的供应商 ID;数字

供应商信息在当前系统中没有规范化。

这里有哪些最佳做法?是否可以删除产品和供应商表并在每个周期使用新数据重写?还是只添加新行并更新现有行更好?

注意事项:

    此数据将用于生成Orders,该数据将通过夜间数据库导入持续存在。 OrderItems 需要连接到数据文件中指定的产品 ID,因此我们不能依赖自动递增的主键来确保每次导入都相同;需要使用唯一的字母数字 ID 将 products 连接到 order_items。 理想情况下,我希望进口商标准化供应商数据 我不能使用普通 SQL 语句,所以我想我需要编写一个 rake 任务才能使用 Product.create(...)Vendor.create(...) 样式语法。 这将在 EngineYard 上实现

【问题讨论】:

【参考方案1】:

我不会在每个周期都删除产品和供应商表。这是一个轨道应用程序吗?如果是这样的话,有一些非常好的 ActiveRecord 助手会派上用场。

如果你有一个产品活动记录模型,你可以这样做:

p = Product.find_or_initialize_by_identifier(<id you get from file>)
p.name = <name from file>
p.size = <size from file>
etc...
p.save!

find_or_initialize 会根据你指定的 id 在数据库中查找产品,如果找不到,它会创建一个新的。这样做的真正方便之处在于,ActiveRecord 只会在任何数据发生更改时保存到数据库中,并且它会相应地自动更新您在表 (updated_at) 中拥有的任何时间戳字段。还有一件事,因为您将通过标识符(文件中的 id)查找记录,所以我会确保在数据库中的该字段上添加一个索引。

要创建一个 rake 任务来完成此任务,我会将一个 rake 文件添加到您的 rails 应用程序的 lib/tasks 目录中。我们将其称为 data.rake。

在 data.rake 中,它看起来像这样:

namespace :data do
  desc "import data from files to database"
  task :import => :environment do
    file = File.open(<file to import>)
    file.each do |line|
      attrs = line.split(":")
      p = Product.find_or_initialize_by_identifier(attrs[0])
      p.name = attrs[1]
      etc...
      p.save!
    end
  end
end

要调用 rake 任务,请在命令行中使用“rake data:import”。

【讨论】:

我试过这个,但我收到了错误undefined local variable or method 'data' for main:Object。有什么想法为什么会发生这种情况? 问题是namespace data do 必须更改为namespace :data do【参考方案2】:

由于产品并不会经常更改,因此我认为最好的方法是仅更新更改的记录。

    获取所有增量 使用单个 SQL 语句进行大规模更新

如果您在模型中包含规范化代码,则可以使用 Product.create 和 Vendor.create ,否则它只会是矫枉过正。此外,考虑在单个 SQL 事务中插入多条记录,它的速度要快得多。

【讨论】:

如我的问题中所述,我不能使用普通 SQL 语句。【参考方案3】: 创建一个 cronned 的导入器 rake 任务 使用 Faster CSV 或通过 vanilla ruby​​ 逐行解析文件,例如:

file.each 做 |line| products_array = line.split(":") 结束

在“:”上分割每一行并推入一个散列

使用 find_or_initialize 填充您的数据库,例如:

Product.find_or_initialize_by_name_and_vendor_id("foo", 111)

【讨论】:

你为什么使用find_or_initialize_by_name_and_vendor_id?这是否建议产品accepts_nested_attributes_for :vendor

以上是关于如何编写 Rake 任务以将数据导入 Rails 应用程序?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 kubernetes cron 作业中启动 rails rake 任务

Rails Guide -- Ruby on Rake

如何在 Rails 中使用环境将参数传递给 Rake 任务? [复制]

如何在 ruby​​ on rails 的亚马逊 aws 服务器中使用 gem 执行 rake 任务?

强制 Rake 任务在特定的 Rails 环境中运行

在 Elastic Beanstalk 环境中运行 Rails rake 任务