21

I am working on an application that is already deployed to some test and staging systems and various developers workstations. I need to add some additional reference data but i'm not sure how to add it.

Most of the advice says use seed.rb, however my understanding is that this is only run once, when the application is initially deployed. Since we don't want to rebuild the test and staging databases just so that we can add 1 row of reference data, is there another way to add the data?

I'm thinking of using a db migration, is this the correct approach?

Thanks

toby
  • 683
  • 2
  • 6
  • 16
  • 2
    You can run `seed.rb` as many time as you want, it's just a normal ruby script file… Although keep in mind that if you have ran it before and you run it again you'll get duplications. In your case if you want to just add a row of data then a `rake task` or use a script runner http://guides.rubyonrails.org/command_line.html#rails-runner I don't think migration is appropriate for this though. – j03w Sep 02 '13 at 09:18

6 Answers6

30

Structure your seed.rb file to allow ongoing creation and updating of data. You are not limited to running a seed file only once and if you think it's only used for initial deployment you will miss out on the flexibility it can offer in setting reference data.

A seed file is just ruby so you can do things like:

user = User.find_or_initialize_by(email: 'bob@example.com')
user.name = 'Bob'
user.password = 'secret'
user.role = 'manager'
user.save!

This will create new data if it doesn't exist or update the data if it finds some.

If you structure your seed file correctly you can also create and update dependent objects.

I recommend using the bang save to ensure that exceptions are raised in the event that an object cannot be saved. This is the easiest method of debugging the seed.

I use the seedbank gem to provide more structure to my seed data, including setting data per environment, dependent seeds and more.

I don't recommend using migrations for seed data. There is a lack of flexibility (how do you target seed data to just one environment for instance) and no real way to build up a reusable set of data that can be run at any time to refresh a particular environment. You would also have a set of migrations which have no reference to your schema and you would have to create new migrations every time you wanted to generate new or vary current data.

nmott
  • 9,454
  • 3
  • 45
  • 34
  • @nmott- After adding above like content should I need to do rake db:seed? If I do like that the existing ones are also populated again?? – Sam Jun 04 '14 at 07:48
  • 2
    @Jsd Yes, `rake db:seed` will load the seed file and if a particular model already exists then it will find the record and overwrite it with the data in the seed file. This means that you can change data in your app and use the seed file to refresh it back to a standard data set on a regular basis. – nmott Jun 05 '14 at 01:06
  • This seems like it would eventually get pretty bloated over time though. – Chris Nicola Jul 26 '14 at 21:36
  • Glad to know it does not delete nonseed data at least – Donato Jun 06 '15 at 20:54
  • You should not be recreating a db with db:migrate, just use db:schema:load – weexpectedTHIS Jul 29 '16 at 18:29
2

You can use a migration, but that's not the safest option you have. Say, for example, you add a record to a table via a migration, then in the future you change that table's schema. When you'll install the app somewhere, you won't be able to run rake db:migrate.

Seeds are always advisable because rake db:seed can be run on a completely migrated schema.

If it's just for a record, go for the rails console.

Miotsu
  • 1,776
  • 18
  • 30
2

It's best to use an idempotent method like this in seed.rb or another task called by seed.rb:

Contact.find_by_email("test@example.com") || Contact.create(email: "test@example.com", phone: "202-291-1970", created_by: "System")
# This saves you an update to the DB if the record already exists.

Or similar to @nmott's:

Contact.find_or_initialize_by_email("test@example.com").update_attributes(phone: "202-291-1970", created_by: "System")
# this performs an update regardless, but it may be useful if you want to reset your data.

or use assign_attributes instead of update_attributes if you want to assign multiple attributes before saving.

konyak
  • 10,818
  • 4
  • 59
  • 65
1

I use the seed file to add instances to new or existing tables all the time. My solution is simple. I just comment out all the other seed data in the db/seeds.rb file so that only the new seed data is live code. Then run bin/rake db:seed.

Steve Carey
  • 2,876
  • 23
  • 34
0

I did something like this in seed.rb

users_list = [
   {id: 1, name: "Diego", age: "25"},
   {id: 2, name: "Elano", age: "27"}
]

while !users_list.empty? do
  begin
    User.create(users_list)
  rescue
    users_list = users_list.drop(1) #removing the first if the id already exist.
  end
end

If a item in the list with the given id already exist it will return a exception, then we remove that item and try it again, until the users_list array is empty.

This way you don't need to search each object before include it, but you will not be able tho update the values already inserted like in @nmott code.

Jaga Jugue
  • 41
  • 1
  • 4
0

Instead of altering seeds.db, which you probably want to use for seeding new databases, you can create a custom Rake task (RailsCast #66 Custom Rake Tasks).

You can create as many Rake tasks as you want. For instance, lets say you have two servers, one running version 1.0 of your app, the other one running 1.1, and you want to upgrade both to 1.2. Then you can create lib/tasks/1-0-to-1-2.rake and lib/tasks`1-1-to-1-2.rake since you may need different code depending on the version of your app.

ehannes
  • 499
  • 4
  • 18