If I've got a column in a database, I want to write a migration that generates another column that contains the md5 hash of the first column. The encryption I can do, but roughly what should the migration look like, structurally? Do I just do a for-each?
I would do it like this with a sql query because this is about the whole table and iteration would slow it down useless.
add_column :table_name, :password_md5, :string
ActiveRecord::Base.connection().execute("UPDATE table_name SET password_md5=MD5(password_plain)")
Related
I'm working on a Rails app and would like to change the datatype for an existing column. It's currently a DateTime type, and I want to change it to a Date type. I found a way to do this here, but in this case, the person was not worried about preexisting data.
Right now, I plan to generate a migration...
rails g migration change_my_column_in_my_talbe
...and make the following change:
class ChangeMyColumnInMyTable < ActiveRecord::Migration
def change
change_column :my_table, :my_column, :date
end
end
My question is: will the existing data be converted to a Date type, or will I need to create a rake task to convert the values for all of my existing DateTime values?
I found a similar question in which the conversion was from Boolean to String, and it seemed like the change would be automatic for existing data. I just want to be sure before I jump into making this change.
I'm using Rails version 4.2.0 and MySQL version 5.6.27 Homebrew. Any advice on this issue would be greatly appreciated!
change_column is going to be translated into an ALTER TABLE ... ALTER COLUMN SQL statement, at which point how the casting is handled is up to your database, so the full answer depends on which database you're using. However, it's a pretty safe bet that your database can convert between datetime and date without trouble.
That said, there's no better way to know than to test it!
Using the change_column method, data conversion will be handled by the specific database adapter you are using. For example, with mysql adapter, change_column will call ALTER TABLE tbl_name MODIFY COLUMN col_name DATE. The data conversion from DATETIME to DATE will truncate the times.
Futhermore, MYSQL DATETIME to DATE performs rounding, conversion to a DATE value takes fractional seconds into account and rounds the time, '1999-12-31 23:59:59.499' becomes '1999-12-31', whereas '1999-12-31 23:59:59.500' becomes '2000-01-01'. Either way, the change method IS NOT reversible for the change_column method.
http://guides.rubyonrails.org/active_record_migrations.html#using-the-change-method
If you plan on reversing this migration, use the reversible method instead.
it is possible to tell rails to keep id columns at the top of the schema and timepstamps at the bottom, when adding some fields to a table through a migration?
What I want is to have always this fields in the same position when playing with rails on the console, I know it's possible to reorder them in schema.rb and reload it but I'm looking for a cleaner approach suitable for production environments too.
thank you
You can something like this (probably currently in Mysql only) from Rails 2.3.6+:
add_column :table_name, :column_name, :column_type, after: :other_column_name
or
change_table :the_table do |t|
t.column_type :column_name, after: :other_column_name
end
Sources:
- In a Rails Migration (MySQL), can you specify what position a new column should be?
- https://rails.lighthouseapp.com/projects/8994/tickets/3286-patch-add-support-for-mysql-column-positioning-to-migrations
You may be interested in the fix-db-schema-conflicts gem which automatically sorts columns alphabetically while generating the db/schema.rb file.
Timestamps would not necessarily be last in the list, but the schema will not rely on the order in which columns were added (by migrations).
How can I specify after which field I want to add a new column? (I don't want the field added in at the bottom after the timestamps)
add_column :users, :is_active, :boolean
Here is how:
add_column :users, :is_active, :boolean, :after => :some_column
where :some_column is the name of the column you want to add :is_active after.
In general, you can't and you shouldn't care. Tables in relational databases don't really have any particular order; that's why you should always specify the columns when you INSERT and never say things like select * ... (yes, ActiveRecord does that but that but that doesn't mean that it is a good idea, just because AR jumps off a cliff doesn't mean you should, etc.).
If you insist on trying to do the database's job for it then the portable way is:
Copy or rename the table to another table.
Drop the original (unless you renamed in (1)).
Create the new table with the columns in the order you want.
Hope the database pays attention to your order in (3).
Copy the data to from the table in (1) to the "new" table from (3).
...
Profit.
If you're using the MySQL or MySQL2 database adapters, you can use the :after => :some_column option to add_column. If you're using SQLite or PostgreSQL, then see the portable way above. If you're using anything else then you'll have to read the adapater's documentation or source code to see what is supported.
But you should ask yourself why you think you want to do this before proceeding.
Say, we have something like this:
add_column :users, :single, :boolean
add_index :users, :single
and then later we do
rename_column :users, :single, :married
Will ActiveRecord and/or the database handle the renaming of the index as well or do I have to manually drop the index and add it again?
For PostgreSQL, rename_column is implemented as a simple ALTER TABLE ... RENAME COLUMN ... and that does preserve the indexes.
The MySQL versions (both of them) do an ALTER TABLE ... CHANGE ... and that also preserves the indexes.
The SQLite version appears to copy the entire table (with indexes), drop the old one, and then copy the copy back to the original table name. The copying does appear to handle the column rename while copying the indexes:
def copy_table(from, to, options = {})
#...
copy_table_indexes(from, to, options[:rename] || {})
and inside copy_table_indexes:
columns = index.columns.map {|c| rename[c] || c }.select do |column|
to_column_names.include?(column)
end
So, the standard drivers will preserve your indexes when you do a rename_column and the SQLite driver goes to some effort to do so.
The API documentation doesn't specify any particular behavior though so other drivers may do other things. The closest the documentation comes to saying anything about indexes is this in active_record/migration.rb:
rename_column(table_name, column_name, new_column_name): Renames a column but keeps the type and content.
I think any driver would preserve the indexes but there's no guarantee; a driver writer would certainly be foolish not to preserve the indexes.
This isn't a definitive or authoritative answer but your indexes should be preserved if you use the standard PostgreSQL, MySQL (either one of them), or SQLite drivers.
Note that even though the index itself survives the column renaming there's no guarantee that the index name will be changed. This shouldn't be a problem unless you're doing something (such as manually dropping it) that cares about the index name rather than what columns are involved.
The above behavior changed in Rails 4:
In Rails 4.0 when a column or a table is renamed the related indexes are also renamed. If you have migrations which rename the indexes, they are no longer needed.
So ActiveRecord will automatically rename indexes to match the new table or column names when you rename the table or column. Thanks to sequielo for the heads-up on this.
I have a rails program that is accessing a legacy database with UPPERCASE table columns.
I want to be able to type user.firstname rather than user.FIRSTNAME
How do I make ActiveRecord retrieve the lowercase version of these column names to allow me to use lowercase attributes in the model?
It might be easier to change the column names with migrations. Otherwise you will have to change the gems that you are using and then pack them in vendor/gems to keep as they degrade.
script/generate migration down_case_table_names_and_columns
write the migration
rake db:migrate
For each table:
rename_table :OLD_NAME, :new_name
For each column:
rename_column :COLUMN_NAME, :column_name
problems
You might not have to change the name of the tables, fyi - you might get an error about changing the table names. I have never changed a table name so I don't know if this will work. In therms of changing column names there will be no problem.
This is probably the best way to deal with this if changing column names isn't feasible: https://github.com/reidmix/legacy_mappings