Rails: remove array from DB column in migration - ruby-on-rails

In Rails, you can add an array column in a migration like so:
change_table :scheduled_posts do |t|
t.string :list_id, array: true
end
What's the syntax to change it to a non-array?

Turns out it was pretty simple:
change_column :scheduled_posts, :list_id, :string, array: false

Related

Change multiple columns with one single migration change

I have the following migration. Is there a way to run these changes in one change than 3?
def change
change_column :comments, :attr_1, :string, null: true
change_column :comments, :attr_2, :string, null: true
change_column :comments, :attr_3, :string, null: true
end
The short answer is no. The change_column method is configured to take arguments for table name, column name, and an options hash. The source code for change_column can be found here: https://github.com/rails/rails/blob/0fe76197d2622674e1796a9a000995a7a1f6622b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
The only line in the change_column method is:
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_sql(table_name, column_name, type, options)}")
which executes an ALTER script on the table name passed as the first argument. You can't pass an array of tables/columns as arguments, so you have to do them one at a time. This paradigm is fairly typical in Rails migrations.
You can use the change_table function with bulk set to true which will run the alter as a single MySQL query and be much faster than running individually. Example is below.
def change
change_table(:comments, bulk: true) do |t|
t.change :attr_1, :string, null: true
t.change :attr_2, :string, null: true
t.change :attr_3, :string, null: true
end
end

Add_column migration column order

I use Rails 4, SQLite version 3.8.2 and I would like to add new column to my db.
I create new migration:
rails g migration AddFooToStudents foo:string
so I get then :
class AddFooToStudents < ActiveRecord::Migration
def change
add_column :students, :foo, :string, after: :name
end
end
then I run migration:
rake db:migrate
== 20150803095305 AddFooToStudents: migrating
=================================
-- add_column(:students, :foo, :string, {:after=>:name})
-> 0.0009s
== 20150803095305 AddFooToStudents: migrated (0.0011s)
========================
Everythink seems to be OK, in database has been added foo column but instead of after name column, it has been added at the end of table
ActiveRecord::Schema.define(version: 20150803095305) do
create_table "students", force: :cascade do |t|
t.string "name"
t.string "lastname"
t.integer "age"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "second_name", default: "Untitled"
t.string "foo"
end
end
I completely don't know what I do wrong
You're using the after option, and so you could reasonably expect it to put it after :name.
However, this isn't documented very well (if at all) but the after option only works in some DBMSs (possibly only MySQL).
What i do is add my own sql to do this, after the add_column call, like so:
add_column :students, :foo, :string
ActiveRecord::Base.connection.execute("ALTER table students MODIFY COLUMN foo varchar(255) AFTER name")
Your SQL will need to be DBMS-specific, ie tailored to MySql, PostGresql, SQLite etc.
Well, SQLite does not handle AFTER syntax so in this situation the best solution is leave unchanged order of columns or create new table.

Uniqueness with scope in migration

I've been trying to find a way to achieve this but I cannot find any attempts even so I am thinking that maybe my approach is completely wrong. That said, what should I do in my migration if I want a combination of two fields to be unique? Please note that I do not want them to be indexes, just database fields.
For example, for the migration below, I can separately add unique: true to the fields, but the combo?
class CreateSomething < ActiveRecord::Migration
def change
create_table :something do |t|
t.date :datestamp, :null => false
t.integer :some_number, :null => false
t.timestamps
end
end
end
I'm not sure what you mean by
Please note that I do not want them to be indexes, just database fields.
Indexes are extra pieces on information that the database stores about the columns.
More importantly an index is exactly what you need!
class CreateSomething < ActiveRecord::Migration
def change
create_table :something do |t|
t.date :datestamp, :null => false
t.integer :some_number, :null => false
t.timestamps
end
add_index :something, [:datestamp, :some_number], unique: true
end
end

How to update reference column in Ruby

I have a migration that I want to make with a reference in my table. I create the reference using this:
create_table :user_events do |t|
t.references :user, :null => false
end
And in my migration, I want to be able to allow the reference to be NULL.
def self.up
change_column :user_events, :user, :integer, :null => true
end
However I keep getting PGError: ERROR: column "user" of relation "user_events" does not exist. Am I migrating wrong?
This should work:
def self.up
change_column :user_events, :user_id, :integer, :null => true
end
Note that the column you're trying to change is called user_id, not user
It's because your migration creates a column named user_id, referencing the User model.
try
def self.up
change_column :user_events do |c|
c.references :user, :integer, :null => true
end
end

In a rails migration, how can you remove the limit of a field

Is the following correct?
change_column :tablename, :fieldname, :limit => null
If you previously specified a limit in a migration and want to just remove the limit, you can just do this:
change_column :users, :column, :string, :limit => 255
255 is the standard length for a string column, and rails will just wipe out the limit that you previously specified.
Updated:
While this works in a number of Rails versions, you would probably be better suited to use nil like in Giuseppe's answer.
change_column :users, :column, :string, :limit => nil
That means the only thing you were doing wrong was using null instead of nil.
Here's what happened to me.
I realized that a string field I had in a table was not sufficient to hold its content, so I generated a migration that contained:
def self.up
change_column :articles, :author_list, :text
end
After running the migration, however, the schema had:
create_table "articles", :force => true do |t|
t.string "title"
t.text "author_list", :limit => 255
end
Which was not OK. So then I "redid" the migration as follows:
def self.up
# careful, it's "nil", not "null"
change_column :articles, :author_list, :text, :limit => nil
end
This time, the limit was gone in schema.rb:
create_table "articles", :force => true do |t|
t.string "title"
t.text "author_list"
end
Change the column type to :text. It does not have a limit.
change_column :tablename, :fieldname, :text, :limit => nil
Strings without limit is not something most databases support: you have to specify size in varchar(SIZE) definition.
Although you could try, I would personally go with :limit => BIG_ENOUGH_NUMBER. You may also consider using CLOB type for very big texts.
To make it db-driver-independent one should write smth like this:
add_column :tablename, :fieldname_tmp, :text
Tablename.reset_column_information
Tablename.update_all("fieldname_tmp = fieldname")
remove_column :tablename, :fieldname
rename_column :tablename, :fieldname_tmp, :fieldname
I was the same boat today, trying to remove a limit I'd added to a text field and it wouldn't take. Tried several migrations.
Rails 4.2.7.1
Ruby 2.3.1p112
In the end, the only thing that worked was specifying a limit of 255. Trying to adjust to anything else wouldn't work for me.

Resources