homeASCIIcasts

179: Seed Data 

(view original Railscast)

Other translations: En

Other formats:

Written by samsam (www.isamsam.com)

Rails最近升级到2.3.4了。这次发布主要集中在安全和修改bug上,但还是有一些有趣的新特性。其中一个新特性就是允许你给应用的数据库加上seed数据,让应用可以运行。

当你用Rails2.3.4创建一个Rails应用的时候,在db目录里有一个叫做seeds.rb的文件。这是一个约定的用来放置任何你的应用需要的初始数据的地方。运行一个新的rake任务:rake db:seed,这些数据就会被创建。

给一个快速的示范,我们会在seed文件里加一个puts语句。

/db/seeds.rb

puts "Seed data goes here."

然后运行rake任务,我们将看到输出。

% rake db:seed
(in /Users/eifion/rails/apps_for_asciicasts/ep179/seeder)
Seed data goes here.

第一次看到可能看起来是个简单的新特性,但是确实是。这个新特性值得的注意的是,它意味着现在有一个约定的地方去放应用的seed数据了。

创建seed数据

比方说,我们在写一个用户注册时需要去选择他们用的操作系统的应用,要实现这个,我们要创建一个OperationSystem模型,它有叫做name的一列。我们用通常的方式生成这个模型。

script/generate model operating_system name:string

操作系统列表不是要用户来生成的,所以我们需要定义一些初始数据。但是我们应该在哪里做呢?有时Rails开发者加seed数据的一个地方是在migration文件里面,向这样:

class CreateOperatingSystems < ActiveRecord::Migration  
  def self.up  
    create_table :operating_systems do |t|  
      t.string :name  

      t.timestamps  
    end  

    # Create the seed data  
    ["Linux", "Mac OS X", "Windows"].each do |os|  
      OperatingSystem.find_or_create_by_name os  
    end  
  end  

  def self.down  
    drop_table :operating_systems  
  end  
end

这样可以用,但是这确实不是最好的方法。Migrations最好留给他们设计用来做的事情:创建数据库结构。在里面创建seed数据也会导致你的seed数据分散在多个migration文件里。

现在从Rails2.3.4开始,我们有一个创建seed数据的中心地方了,所以我们可以吧migration文件中seed数据代码移到seeds.rb文件中了。

/db/seeds.rb

["Linux", "Mac OS X", "Windows"].each do |os|  
  OperatingSystem.find_or_create_by_name os  
end

注意我们用find_or_create_by_name,这样只有在他们不存在的时候才会创建,就是说seed数据文件可以不止一次执行,并且不用担心重复创建同样的操作系统。

另一类你可能要给应用加的seed数据的例子是地址表单用到的国家列表。我们要给国家生成另一个模型,有一个name和一个code。

script/generate model country name:string code:string

输入所有国家数据将会相当乏味,即使我们只需要输入一次。幸运的是,这个链接是包含国家列表的文件,代码和名称用竖线分隔。

AF|Afghanistan
AL|Albania
DZ|Algeria
AS|American Samoa
AD|Andorra
…

我们可以文件中的数据填入Country模型。

/db/seeds.rb

Country.delete_all  
open("http://openconcept.ca/sites/openconcept.ca/files/country_code_drupal_0.txt") do |countries|  
  countries.read.each_line do |country|  
    code, name = country.chomp.split("|")  
    Country.create!(:name => name, :code => code)  
  end  
end

这次我们用一种稍微不一样的方式填入数据。首先我们删除任何存在的国家,然后打开那个文本文件,循环文件的每一行,用代码和名称来生成国家。这样提供了一种快速简单的方式来给country模型填入数据。上面的代码用了OpenURI,是用来获取文件的,所以要使用它就要在头部包含它。

/db/seeds.rb

require 'open-uri'

现在我们已经写好我们的seed脚本了,我们可以运行看看是不是可以用了。运行之前我们需要运行migrations文件来创建这两个模型。

rake db:migrate

然后我们可以运行我们的seed任务了。

rake db:seed

运行需要几秒钟时间,当运行完,我们的数据库将被填入数据。我们可以运行script/console来检查一下。

我们的操作系统在那儿:

>> OperatingSystem.all
+----+----------+-------------------------+-------------------------+
| id | name     | created_at              | updated_at              |
+----+----------+-------------------------+-------------------------+
| 1  | Linux    | 2009-09-14 20:55:20 UTC | 2009-09-14 20:55:20 UTC |
| 2  | Mac OS X | 2009-09-14 20:55:20 UTC | 2009-09-14 20:55:20 UTC |
| 3  | Windows  | 2009-09-14 20:55:20 UTC | 2009-09-14 20:55:20 UTC |
+----+----------+-------------------------+-------------------------+
3 rows in set

这是国家:

>> Country.all

+-----+---------------------+------+---------------------+---------------------+
| id  | name                | code | created_at          | updated_at          |
+-----+---------------------+------+---------------------+---------------------+
| 1   | Afghanistan         | AF   | 2009-09-14 21:03... | 2009-09-14 21:03... |
| 2   | Albania             | AL   | 2009-09-14 21:03... | 2009-09-14 21:03... |
| 3   | Algeria             | DZ   | 2009-09-14 21:03... | 2009-09-14 21:03... |
| 4   | American Samoa      | AS   | 2009-09-14 21:03... | 2009-09-14 21:03... |
| 5   | Andorra             | AD   | 2009-09-14 21:03... | 2009-09-14 21:03... |

夹具

我们用一个最后技巧来结束本集。如果你的应用已经包含你要用的像seed数据的夹具了,你可以用这些作为你的seed数据的基础。

假设我们在/test/fixtures/operating_systems.yml文件里有下面的seed数据。

/test/fixtures/operating_systems.yml

# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html  

windows:  
 name: Windows  

mac:  
 name: Mac OS X  

linux:  
 name: Linux

我们可以用这个替换seeds.rb文件中生成操作系统的代码来导入。

/db/seeds.rb

require 'active_record/fixtures'  

Fixtures.create_fixtures("#{Rails.root}/test/fixtures", "operating_systems")

如果我们重新运行我们的seed任务,操作系统对象将重新创建。

>> OperatingSystem.all
+------------+----------+-------------------------+-------------------------+
| id         | name     | created_at              | updated_at              |
+------------+----------+-------------------------+-------------------------+
| 303122256  | Linux    | 2009-09-14 21:28:31 UTC | 2009-09-14 21:28:31 UTC |
| 387181413  | Mac OS X | 2009-09-14 21:28:31 UTC | 2009-09-14 21:28:31 UTC |
| 1676117404 | Windows  | 2009-09-14 21:28:31 UTC | 2009-09-14 21:28:31 UTC |
+------------+----------+-------------------------+-------------------------+
3 rows in set

从夹具文件获取数据有一个要注意的不同的地方:ids相当靠不住。这是因为这种方式当夹具文件中没有明确指定ids会生成ids。

在开发者中有一些关于什么精确构成seed数据的争论。对于一些人来说让应用运行最小的数据量是需要的,另外的人喜欢添加用户记录和用户生成数据。如果你想保持那些构建应用绝对需要的数据和其他可能需要的数据分离的化,你可以用rake任务。126集用这种方法用大量的测试数据来填入数据库。如果当应用有大量数据你试着测试应用的性能,或者模拟应用有大量数据的样子,这是一个好方法。

当我们在seed数据这个主题上,值得去看看seed-fu library,这是另一种生成seed数据的方法。如果你没有用Rails的最新版本,这种方法提供了一个有用的生成seed数据的替代方案。

或者,BootStrapper库也值得研究研究。