I would like to make a column unique
in Ruby on Rails migration script. What is the best way to do it? Also is there a way to index a column in a table?
I would like to enforce unique
columns in a database as opposed to just using :validate_uniqueness_of
.
8 Answers
The short answer for old versions of Rails (see other answers for Rails 4+):
add_index :table_name, :column_name, unique: true
To index multiple columns together, you pass an array of column names instead of a single column name,
add_index :table_name, [:column_name_a, :column_name_b], unique: true
If you get “index name… is too long”, you can add name: "whatever"
to the add_index method to make the name shorter.
For fine-grained control, there’s a “execute
” method that executes straight SQL.
That’s it!
If you are doing this as a replacement for regular old model validations, check to see how it works. The error reporting to the user will likely not be as nice without model-level validations. You can always do both.
12
+1 for suggesting continuing to use the validates_uniqueness_of. The error handling is much cleaner using this method for the cost of a single indexed query I would suggest he does both
I tried that it doesn’t seem to work! I could insert two record with the column_name that I defined as unique! I’m using Rails 2.3.4 and MySql any ideas?
–I used you second suggestion by using execute: execute “ALTER TABLE users ADD UNIQUE(email)” and it works! not sure why the first one didn’t would be interested in knowing
–I found that the composite index alone didn’t present any nice errors, therefore validated uniqueness as well. Cheers!
If you get an
indexed columns are not unique
error when trying to create a unique index, it might be because the data in the table already contains duplicates. Try removing the duplicate data and running the migration again.
rails generate migration add_index_to_table_name column_name:uniq
or
rails generate migration add_column_name_to_table_name column_name:string:uniq:index
generates
class AddIndexToModerators < ActiveRecord::Migration
def change
add_column :moderators, :username, :string
add_index :moderators, :username, unique: true
end
end
If you’re adding an index to an existing column, remove or comment the add_column
line, or put in a check
add_column :moderators, :username, :string unless column_exists? :moderators, :username
2
I upvoted this because I wanted the command line form. But it’s silly that it adds the column even when I specify
add_index...
and notadd_column...
.Yeap, maybe in next version.
If you are creating a new table, you can use the inline shortcut:
def change
create_table :posts do |t|
t.string :title, null: false, index: { unique: true }
t.timestamps
end
end
4
Note that you can skip null definition, i.e.:
t.string :title, index: { unique: true }
So this will create an index but it’s not going to be unique, right?
Oh sorry that’s my mistake. I just got lost and asked a stupid question. You answered exactly what I wanted to ask haha. Thank you! I had wanted to ask whether
index: true
would create a unique index
Since this hasn’t been mentioned yet but answers the question I had when I found this page, you can also specify that an index should be unique when adding it via t.references
or t.belongs_to
:
create_table :accounts do |t|
t.references :user, index: { unique: true } # or t.belongs_to
# other columns...
end
(as of at least Rails 4.2.7
)
I’m using Rails 5 and the above answers work great; here’s another way that also worked for me (the table name is :people
and the column name is :email_address
)
class AddIndexToEmailAddress < ActiveRecord::Migration[5.0]
def change
change_table :people do |t|
t.index :email_address, unique: true
end
end
end
1
Note this does not work if the index already exists.
You might want to add name for the unique key as many times the default unique_key name by rails can be too long for which the DB can throw the error.
To add name for your index just use the name:
option.
The migration query might look something like this –
add_index :table_name, [:column_name_a, :column_name_b, ... :column_name_n], unique: true, name: 'my_custom_index_name'
More info – http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index
add_index :table_name, :column_name, unique: true
To index multiple columns together, you pass an array of column names instead of a single column name.
If you have missed to add unique to DB column, just add this validation in model to check if the field is unique:
class Person < ActiveRecord::Base
validates_uniqueness_of :user_name
end
refer here
Above is for testing purpose only, please add index by changing DB column as suggested by @Nate
please refer this with index for more information
2
I would not recommend just adding the validation without a corresponding index. The better option is to clean up any existing duplicates and then add the index. Otherwise you risk invalidating existing data (which will cause any updates to those rows to fail), and you could still end up with duplicates if you have any code that skips Rails validations. (e.g., when running an update_all, or direct SQL inserts)
– NateThis is good enough to show a nice error message but not to enforce integrity of your data. See thoughtbot.com/blog/the-perils-of-uniqueness-validations for an explanation.
– Pascal