Recently I had to create a couple of records in a non-rails app database table based on a previous record. It got me thinking of how would I do this in a rails app. I tried a couple of things in the Console, but nothing works.
I want to do something like this:
001> user = User.new(User.first)
I know this doesn't work but hopefully it will show you what I an thinking. User is a large table/model, and I only need to change a few fields. So, if I can set up a new record with the same values in User.first, I can then edit the fields I need to before .save-ing the record.
Thanks for any help.
I think what you want is:
user = User.first.dup
user.assign_attributes(email: "myemail#test.test")
user.save
The first line uses dup to create a copy of the object. The copy is not yet saved to the database. Replace dup with clone if you're using an old version of Rails (<3.1).
In the second line, assign_attributes alters the attributes of the object, still without saving it to the database. If you were working with an object already saved in the database, you could use update instead of assign_attributes to change the attributes of the object and save the changes in one go. That won't work here, because we haven't saved our duplicate user yet. More details on that here.
The third line finally saves the new object to the database. It saves time to just do this once, at the end.
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
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.
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.
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.
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.
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.
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.
I am using the active-record-reputation-system gem, and would like to update it to v 2.0.0.
The gem creates three tables, RS_Evaluations, RS_Reputation_Messages, and RS_Reputations. These three tables have been renamed for v 2.0.0, but they do not get updated in my database by simply bundle installing the v2.0.0 gem.
How can I go about updating these tables? Should I:
1) Update just the table names with the content of the columns intact? (To the best of my knowledge, the column names are unchanged with the update). If so, how can I do this?
2) Drop the older tables and create new correct tables? This is not ideal, but won't kill me, as I don't have a huge amount of data in the existing tables. Can I simply delete the older tables via my database viewer (ie. PGAdminIII)? Or do I need to migrate a file that drops the tables?
Input would be much appreciated!
UPDATE
The migration guide has a line about
Also, you need to update your database data as follow:
UPDATE rs_reputation_messages SET sender_type = 'ReputationSystem::Evaluation' WHERE sender_type = 'RSEvaluation'
How would I go about doing this? I am only familiar with updating the database through migration files. Is this an SQL call made directly to the database?
What is the best way to test for an empty database in rails? I generated a model chardata and the controller, Chardatum. I want to loop through the database and extract a certain column, but I need a way to test if the database is empty first.
If you are fetching some Chardata allready you could do:
#chardata.any?
It depends on two things.
What kind of database are you using?
And by empty, do you mean the tables have not been created yet? or just that data has not been inserted yet in to the tables?
To see if the chardatas table has no rows before proceeding:
Chardata.count.zero?
Assuming you're using MySQL, you can use the following command to see if tables have been created already in your database
Chardata.count_by_sql "SELECT COUNT(DISTINCT 'table_name') FROM 'information_schema'.'columns' WHERE 'table_schema' = 'your_db_name'".zero?
You can also check if the table has been created with CharDatum.table_exists?.
I want to clear a table in my railsapp , without dropping the database and migrating...
MyModel.all.each{|m| m.destroy}
I would expect this code to delete every record in the my_model table, but this is not happening...
using Rails 2.3.4 + MySQL 5.1
EDIT:
the issue was based on the plugin better_nested_set which didn't allow me to delete the entries in that order
MyModel.delete_all worked on the other hand , maybe because it executes truncate on the database (?)
Use MyModel.destroy_all to delete all the records for your model.