在 Rails 中播种数据库的最佳方法是啥?
Posted
技术标签:
【中文标题】在 Rails 中播种数据库的最佳方法是啥?【英文标题】:What is the best way to seed a database in Rails?在 Rails 中播种数据库的最佳方法是什么? 【发布时间】:2010-10-20 03:42:15 【问题描述】:我有一个 rake 任务,它会在我的 rails 应用程序中填充一些初始数据。例如,国家、州、移动运营商等。
我现在设置它的方式是,我在 /db/fixtures 的文件中有一堆创建语句和一个处理它们的 rake 任务。例如,我拥有的一个模型是主题。我在 /db/fixtures 中有一个 theme.rb 文件,如下所示:
Theme.delete_all
Theme.create(:id => 1, :name=>'Lite', :background_color=>'0xC7FFD5', :title_text_color=>'0x222222',
:component_theme_color=>'0x001277', :carrier_select_color=>'0x7683FF', :label_text_color=>'0x000000',
:join_upper_gradient=>'0x6FAEFF', :join_lower_gradient=>'0x000000', :join_text_color=>'0xFFFFFF',
:cancel_link_color=>'0x001277', :border_color=>'0x888888', :carrier_text_color=>'0x000000', :public => true)
Theme.create(:id => 2, :name=>'Metallic', :background_color=>'0x000000', :title_text_color=>'0x7299FF',
:component_theme_color=>'0xDBF2FF', :carrier_select_color=>'0x000000', :label_text_color=>'0xDBF2FF',
:join_upper_gradient=>'0x2B25FF', :join_lower_gradient=>'0xBEFFAC', :join_text_color=>'0x000000',
:cancel_link_color=>'0xFF7C12', :border_color=>'0x000000', :carrier_text_color=>'0x000000', :public => true)
Theme.create(:id => 3, :name=>'Blues', :background_color=>'0x0060EC', :title_text_color=>'0x000374',
:component_theme_color=>'0x000374', :carrier_select_color=>'0x4357FF', :label_text_color=>'0x000000',
:join_upper_gradient=>'0x4357FF', :join_lower_gradient=>'0xffffff', :join_text_color=>'0x000000',
:cancel_link_color=>'0xffffff', :border_color=>'0x666666', :carrier_text_color=>'0x000000', :public => true)
puts "Success: Theme data loaded"
这里的想法是我想为用户安装一些股票主题。我对这种方法有疑问。
设置 ID 不起作用。这意味着如果我决定添加一个主题,让我们称之为“红色”,那么我只想将主题语句添加到这个夹具文件并调用 rake 任务来重新设置数据库。如果我这样做,因为主题属于其他对象,并且在重新初始化时它们的 id 会发生变化,所以所有链接都会断开。
首先我的问题是,这是处理数据库播种的好方法吗?在之前的帖子中,这是推荐给我的。
如果是这样,我如何对 ID 进行硬编码,这样做有什么缺点吗?
如果没有,最好的数据库种子方式是什么?
我将非常感谢包含最佳实践的长而深思熟虑的答案。
【问题讨论】:
【参考方案1】:更新,因为这些答案有些过时(尽管有些仍然适用)。
rails 2.3.4 中添加的简单功能,db/seeds.rb
提供新的 rake 任务
rake db:seed
适合填充常见的静态记录,如州、国家等...
http://railscasts.com/episodes/179-seed-data
*请注意,如果您已经创建了固定装置,也可以使用 db:seed 任务填充,方法是将以下内容放入您的种子.rb 文件(来自 railscast 剧集):
require 'active_record/fixtures'
Fixtures.create_fixtures("#Rails.root/test/fixtures", "operating_systems")
对于 Rails 3.x,使用 'ActiveRecord::Fixtures' 而不是 'Fixtures' 常量
require 'active_record/fixtures'
ActiveRecord::Fixtures.create_fixtures("#Rails.root/test/fixtures", "fixtures_file_name")
【讨论】:
【参考方案2】:通常需要两种类型的种子数据。
您的应用程序的核心可能依赖的基本数据。我称之为普通种子。 环境数据,例如,为了开发应用程序,拥有一组处于已知状态的数据,我们可以使用这些数据在本地处理应用程序(上面的 Factory Girl 回答涵盖了此类数据) )。根据我的经验,我总是遇到对这两种类型数据的需求。所以我将a small gem that extends Rails' seeds 放在一起,让您在 db/seeds/ 下添加多个常见种子文件,并在 db/seeds/ENV 下添加任何环境种子数据,例如 db/seeds/development。
我发现这种方法足以为我的种子数据提供一些结构,并让我能够通过运行将我的开发或暂存环境设置为已知状态:
rake db:setup
与常规 sql 转储一样,fixture 很脆弱且难以维护。
【讨论】:
我喜欢用“系统数据”和“运行时数据”这两个词来描述代码依赖于现有数据和来自用户的数据。有时它们之间的界限是模糊的。【参考方案3】:factory_bot 听起来它会做你想要实现的目标。您可以在默认定义中定义所有公共属性,然后在创建时覆盖它们。您还可以将 id 传递给工厂:
Factory.define :theme do |t|
t.background_color '0x000000'
t.title_text_color '0x000000',
t.component_theme_color '0x000000'
t.carrier_select_color '0x000000'
t.label_text_color '0x000000',
t.join_upper_gradient '0x000000'
t.join_lower_gradient '0x000000'
t.join_text_color '0x000000',
t.cancel_link_color '0x000000'
t.border_color '0x000000'
t.carrier_text_color '0x000000'
t.public true
end
Factory(:theme, :id => 1, :name => "Lite", :background_color => '0xC7FFD5')
Factory(:theme, :id => 2, :name => "Metallic", :background_color => '0xC7FFD5')
Factory(:theme, :id => 3, :name => "Blues", :background_color => '0x0060EC')
当与 faker 一起使用时,它可以非常快速地使用关联填充数据库,而不必弄乱 Fixtures (yuck)。
我在 rake 任务中有这样的代码。
100.times do
Factory(:company, :address => Factory(:address), :employees => [Factory(:employee)])
end
【讨论】:
FactoryGirl 实际上是用来代替固定装置进行测试的,但它也可以用来将东西加载到生产中。使用以 db:migrate 作为先决条件的 rake 任务来加载所有默认数据。您可能需要使 rake 任务足够智能,使其不会创建现有数据的副本。 不推荐使用FactoryGirl做种子,check this post.【参考方案4】:使用seeds.rb
文件或FactoryBot
很好,但它们分别适用于固定数据结构和测试。
seedbank
gem 可能为您的种子提供更多控制和模块化。它插入 rake 任务,您还可以定义种子之间的依赖关系。您的 rake 任务列表将包含以下内容(例如):
rake db:seed # Load the seed data from db/seeds.rb, db/seeds/*.seeds.rb and db/seeds/ENVIRONMENT/*.seeds.rb. ENVIRONMENT is the current environment in Rails.env.
rake db:seed:bar # Load the seed data from db/seeds/bar.seeds.rb
rake db:seed:common # Load the seed data from db/seeds.rb and db/seeds/*.seeds.rb.
rake db:seed:development # Load the seed data from db/seeds.rb, db/seeds/*.seeds.rb and db/seeds/development/*.seeds.rb.
rake db:seed:development:users # Load the seed data from db/seeds/development/users.seeds.rb
rake db:seed:foo # Load the seed data from db/seeds/foo.seeds.rb
rake db:seed:original # Load the seed data from db/seeds.rb
【讨论】:
【参考方案5】:Rails 有一种内置方法来播种数据,如 here 所述。
另一种方法是使用 gem 进行更高级或更简单的播种,例如:seedbank。
这个 gem 的主要优点和我使用它的原因是它具有高级功能,例如数据加载依赖项和每个环境的种子数据。
添加一个最新的答案,因为这个答案首先出现在 google 上。
【讨论】:
【参考方案6】:最好的方法是使用固定装置。
注意:请记住,fixture 会直接插入而不使用您的模型,因此如果您有填充数据的回调,则需要找到解决方法。
【讨论】:
【参考方案7】:将它添加到数据库迁移中,这样每个人都可以在更新时获得它。在 ruby/rails 代码中处理您的所有逻辑,因此您永远不必搞乱显式 ID 设置。
【讨论】:
如果我需要更改初始数据,使用迁移时事情会变得一团糟。你的第二条评论真的没有意义。通过外键的链接将被销毁 c = Category.create( stuff ) p = Post.create( stuff ) p.category = c 无需显式设置 ID。如果您更改初始数据,您只需创建一个新的迁移。很简单。 假设可以在创建对象时建立关联。这是一个我相信你的逻辑失败的例子......如果我错了,请纠正我。我用模板主题为数据库播种。用户 id=1 创建模板 id=2 和主题 id=4。此时,db中有一条记录如下:template: id=2, user_id=1, theme_id=4。现在如果我重新初始化数据库,主题 id=4 现在是主题 id=10...然后用户的模板主题不正确 嗯,这取决于你所说的“重新初始化”——如果你从零开始,Rails 会自动处理所有的关联。如果你对 ID 值进行硬编码(不好!!!),那么它会爆炸。 好的,我开始明白你的意思了,但我必须由你来运行这个场景。我使用国家/地区查找表为数据库播种。美国 => 国家/地区 ID=1。然后用户创建了一家存在于美国的餐厅。餐厅数据库行的 country_id = 1。这很常见,对吧?稍后我决定要添加更多国家/地区...如果我清除数据库并重新填充国家/地区查找表,那么现在餐厅国家/地区不再准确,除非 ID 相同。我该如何处理?以上是关于在 Rails 中播种数据库的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
使用 Postmarkapp 在 Rails 应用程序中处理电子邮件异常的最佳方法是啥?