In my Rails application which works with Oracle database (version 11g) I need primary keys to be Strings, not Integers. For example, in my Product model I need primary keys like "p001", "p002" etc.
class AddProductWithDifferentPrimaryKey < ActiveRecord:Migration
def change
create_table :table, id: false do |t|
t.string :id, null: false
# other columns
t.timestamps
end
execute "ALTER TABLE table ADD PRIMARY KEY (id);"
end
end
Don't forget to also add this line to your table model so rails knows how to find your new primary key!
class Product < ActiveRecord::Base
self.primary_key = :id
# rest of code
end
Hope this helps. And credit should go to
A K H
For more information you can check out his as well as other answers. primary key info
Related
I previously converted one of my tables' id column to bigint, and populated it with bigint values. I'm now regretting that as I can't seem to update values via ActiveRecord without runnning into RangeErrors.
I'd like to convert the id column back to the standard integer type, and clear out the existing values and populate it with auto-incremented integer values.
I don't have any foreign keys / relations I need to worry about.
Is there a straightforward way to do this via a Rails migration (Rails 4 / Postgres)?
The prior migration:
class ConvertWidgetPkToBigint < ActiveRecord::Migration
def change
change_column :widgets, :id, :bigint
end
end
Current schema.rb:
create_table "widgets", id: :bigserial, force: :cascade do |t|
...
end
Figured it out:
class RecreatePrimaryKey < ActiveRecord::Migration
def change
execute "UPDATE widgets SET id = DEFAULT"
change_column :widgets, :id, :integer
end
end
Basically it re-populates the column with auto-increment values, and then converts the column back to type integer.
I’m using Rails 4.2.7 with PostGres. I have created several tables, all of which have numeric primary keys. Below is an example of one of my Rails migrations
class CreateMyObjects < ActiveRecord::Migration
def change
create_table :my_objects do |t|
t.string :name
t.date :day
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
end
end
I do not have any data in this table, but I do have several tables that link to it through foreign keys. I want to change the primary key from being a numeric primary key to a GUID (UUID) because I’m going to have a situation where data gets created in two different databases and I don’t want there to be primary key collisions when I combine the data. How do I create a migration that will change the primary key’s type from being numeric to my UUID type and how do I update all the foreign keys that link to the table?
Thanks, - Dave
class ChangePrimaryKey < ActiveRecord::Migration
def change
remove_column :my_objects, :id # remove existing primary key
add_column :my_objects, :uuid, :string
execute "ALTER TABLE my_objects ADD PRIMARY KEY (uuid);"
end
end
class MyObject < ActiveRecord::Base
self.primary_key = :uuid
end
As to auto incrementing - no, Rails won't increment your string id for you, so you'll have to take care of it on your own.
You can use a before_create callback in the model:
before_create :generate_uuid
private
def generate_uuid
loop do
self.uuid = SecureRandom.base64(16) # or other way to generate uniq string
break unless self.class.find_by(uuid: uuid) # make sure you don't repeat the uuid
end
end
You can also add constraints to the primary_key column, like not null constraint, uniqueness, length etc
I have an already existing database schema with tables that have a string column as primary key and also some tables with more than one columns as key. I would like to map this schema in rails, but I dont know how to override the default primary key (a column id created by the rails framework).
You can override the primary key like this
class Book < ActiveRecord::Base
self.primary_key = 'author'
end
I don't know what you're trying to do. It's a mistake altering primary key in Rails.
But for that matter try to do it in your migration.
class Foos < ActiveRecord::Migration
def self.up
create_table :foos, :id => false do |t|
t.string :my_id
t.timestamps
end
end
end
I am using rails 3.1 and ruby 1.9.3,Now i want to use uuid concept in rails 3
so i did like :-
create_table :posts, :id => false do |t|
t.string :uuid, :limit => 36, :primary => true
end
ActiveRecord::Base.class_eval do
set_primary_key 'uuid'
before_create :generate_uuid
def generate_uuid
self.id = UUIDTools::UUID.random_create.to_s
end
end
This is working for new data,now i want to migrate existing data with relation.for uuid they are using datatype as string,in postgresql the data type used for primary_key and foreign key is integer ,so if i am trying to change foreign key integer to string it is throwing error.
Could you please tell me some example,how to do this.
kingston.s
First of all, to use UUIDs in ActiveRecord you need to enable the uuid-ossp extension. Create a new migration.
class EnableUuidOssp < ActiveRecord::Migration
def change
enable_extension 'uuid-ossp'
end
end
Second, you do do not need to use string type in your migrations, there is a uuid type. When creating a new table:
create_table :posts, id: :uuid do |t|
end
This will automatically generate a UUID in the same way that an incremental Integer ID is normally generated. When you want to add a UUID field to an existing table:
change_table :posts do |t|
t.uuid :uuid, default: 'uuid_generate_v4()'
end
The default: 'uuid_generate_v4()' will ensure that a new UUID is generated for you by Postgres.
Third, to actually migrate existing data I guess you would need to create migrations that 1) add UUID fields to all of the models 2) create new UUID foreign keys 3) associate the models using the UUID foreign keys 4) remove the old foreign keys 5) rename the new foreign keys:
class AddUuidToPosts < ActiveRecord::Migration
def change
change_table :posts do |t|
t.uuid :uuid, default: 'uuid_generate_v4()'
end
end
end
# assuming you have a comments table that belongs to posts
class AddUuidToComments < ActiveRecord::Migration
def change
change_table :comments do |t|
t.uuid :uuid, default: 'uuid_generate_v4()'
end
end
end
class AssociateCommentsWithPostings < ActiveRecord::Migration
def change
# Add a uuid_posting_id field to comments relate comments to postings
# through the posting UUID
change_table :comments do |t|
t.uuid :uuid_posting_id
end
# Loop through all existing postings and update all associated comments
# new foreign key field
Posting.all.each do |posting|
# Not sure about this, but you might need to touch the posting to generate
# a UUID
posting.touch
Comment.where(posting_id: posting.id).
update_all(uuid_posting_id: posting.uuid)
end
remove_column :comments, :posting_id
rename_column :comments, :uuid_posting_id, :posting_id
end
end
# You don't need to override ActiveRecord::Base to set the primary key to :uuid.
# Only do this in existing models that you migrated to UUIDs. In any new tables
# that you create, the id column will be a UUID, as long as you use the migration
# format that I showed you at the top.
class Posting < ActiveRecord::Base
set_primary_key :uuid
end
You should probably go the extra mile and actually remove the old Integer id fields and rename the new UUID ids to "id" but I'm not sure how to do that off the top of my head. Anyway, I think this approach should work. There could be a couple of errors or bits missing though, it's a bit late over here.
So I've got two models, State and Acquisition. State has_many Acquisitions. I felt like an autoincrementing integer primary key for 51 records was rather silly. So I altered the model for the State to be the PK (State being the two letter abbreviation; I'm not storing the actual state name anywhere:
class State < ActiveRecord::Base
self.primary_key = "state"
has_many :acquisition_histories
end
The problem is when I created my Acquisition model, it created the foreign key column state_id as an integer. More specifically, the script/generated migration did:
class CreateAcquisitions < ActiveRecord::Migration
def self.up
create_table :acquisitions do |t|
t.date :date
t.string :category
t.text :notes
t.references :state
t.timestamps
end
end
end
I'm assuming that t.references data type sets it to int. The problem is my create method on my Acquisition class is trying to put a state abbreviation into the state_id field on the table acquisitions (and yes, it's called state_id on the database, even though it says :state in the migration script). The method doesn't fail, but it does put a 0 in the state_id field and the records go into the ether.
Though, I agree that this might be more trouble than it's worth considering the extra effort of working against the defaults elsewhere, just in case you actually want to do what you've asked:
Create states migration:
class CreateStatesTable < ActiveRecord::Migration
def change
create_table :states, id: false do |t|
t.string :state, limit: 2
t.string :name
t.index :state, unique: true
end
end
end
states model:
class State < ActiveRecord::Base
self.primary_key = :state
end
Note that before Rails 3.2, this was set_primary_key = :state instead of self.primary_key= see: http://guides.rubyonrails.org/3_2_release_notes.html#active-record-deprecations
if you find yourself here... leave as quickly as you can and go to:
Using Rails, how can I set my primary key to not be an integer-typed column?
In Rails 5.1 you can specify the type of the primary key at creation:
create_table :states, id: :string do |t|
# ...
end
From the documentation:
A Symbol can be used to specify the type of the generated primary key column.
I'm working on a project that uses UUIDs as primary keys, and honestly, I don't recommend it unless you're certain you absolutely need it. There are a ton of Rails plugins out there that will not work unmodified with a database that uses strings as primary keys.
Note that mkirk's answer creates a faux primary key. This explains why ActiveRecord needs to be told what the primary key is. Inspecting the table reveals
Table "public.acquisitions"
Column | Type | Modifiers
--------+----------------------+-----------
state | character varying(2) |
name | character varying |
Indexes:
"index_acquisitions_on_state" UNIQUE, btree (state)
In practice this works as expected so nothing wrong there, but it could be nicer.
We can keep the id column and change its type to string*. The migration looks like
class CreateAcquisitionsTable < ActiveRecord::Migration
def change
create_table :acquisitions do |t|
t.string :name
end
change_column :acquisitions, :id, :string, limit: 2
end
end
Inspecting the table reveals that you have an actual primary key with all the goodies such as the unique key constraint (no unique index needed), not null constraint, and auto-incrementing key.
Table "public.acquisitions"
Column | Type | Modifiers
--------+----------------------+---------------------------------------------------
id | character varying(2) | not null default nextval('acquisitions_id_seq'::regclass)
name | character varying |
Indexes:
"acquisitions_pkey" PRIMARY KEY, btree (id)
And you won't need to explicitly tell ActiveRecord what the primary is.
You'll want to consider setting a default id if none is provided.
class MyModel < ActiveRecord::Base
before_create do
self.id = SecureRandom.uuid unless self.id
end
end
* Disclaimer: you should not change the default primary key unless you have good reason to
You want to follow the Rails conventions. The extra primary key is not an issue in any way. Just use it.
I had a bit of experience with string used as primary keys and it's a pain in the ***. Remember that by default if you want to pass an object with the default :controller/:action/:id pattern, the :id will be a string and this will probably lead to routing problems if some ids get weirdly formatted ;)
class CreateAcquisitions < ActiveRecord::Migration
def self.up
create_table :acquisitions, :id => false do |t|
t.date :date
t.string :category
t.text :notes
t.references :state
t.timestamps
end
end
end
Rails works best when you don't fight against the defaults. What harm does it do to have an integer primary key on your state table?
Unless you're stuck with a legacy schema that you have no control over, I'd advise you to stick to the Rails defaults—convention over configuration, right?—and concentrate on the important parts of your app, such as the UI and the business logic.