Altering the Primary Key in Rails to Be a String

generate a model with a string field as primary key

No, you can't. By default the primary key is an auto-increment integer.

However, you can open the migration that was generated from the command, and change it (before running the rake db:migrate command). The migration will likely have a create_table command:

class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
# ...
end
end
end

If you read the create_table documentation, you will notice you can pass two options. Specifically, you need to set :id to false to not generate an id field, and you will need to specify the name of the primary key field.

    create_table :users, id: false, primary_key: :email do |t|

How to add primary key to Rails?

In your migration file:

create_table :bookmarks, :primary_key => :bk_id do |t|
...
t.integer :bk_id
...
end

Do not forget to indicate it in your Model too:

class Bookmarks < ActiveRecord::Base
self.primary_key = 'bk_id'
end

ActiveRecord string primary key used in foreign keys

You can specify the primary key on a join. For example:

class Order < ActiveRecord::Base
belongs_to :system_currency, :primary_key => "iso_code"
end

More information can be found in the Rails API at http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-belongs_to

Is this what you are after or have I misunderstood your question?

Using Rails, how can I set my primary key to not be an integer-typed column?

Unfortunately, I've determined it's not possible to do it without using execute.

Why it doesn't work

By examining the ActiveRecord source, we can find the code for create_table:

In schema_statements.rb:

def create_table(table_name, options={})
...
table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
...
end

So we can see that when you try to specify a primary key in the create_table options, it creates a primary key with that specified name (or, if none is specified, id). It does this by calling the same method you can use inside a table definition block: primary_key.

In schema_statements.rb:

def primary_key(name)
column(name, :primary_key)
end

This just creates a column with the specified name of type :primary_key. This is set to the following in the standard database adapters:

PostgreSQL: "serial primary key"
MySQL: "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
SQLite: "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL"

The workaround

Since we're stuck with these as the primary key types, we have to use execute to create a primary key that is not an integer (PostgreSQL's serial is an integer using a sequence):

create_table :employees, {:id => false} do |t|
t.string :emp_id
t.string :first_name
t.string :last_name
end
execute "ALTER TABLE employees ADD PRIMARY KEY (emp_id);"

And as Sean McCleary mentioned, your ActiveRecord model should set the primary key using set_primary_key:

class Employee < ActiveRecord::Base
set_primary_key :emp_id
...
end


Related Topics



Leave a reply



Submit