Changing model id to uuid in Rails using uuid-ossp - ruby-on-rails

I need to change the id of the existing model to uuid. I'm using this guide:
http://rny.io/rails/postgresql/2013/07/27/use-uuids-in-rails-4-with-postgresql.html
But no idea how to adapt the migration below to changing (not creating a new one):
class CreateDocuments < ActiveRecord::Migration
def change
create_table :documents, id: :uuid do |t|
t.string :title
t.string :author
t.timestamps
end
end
end

Try This.
class documents < ActiveRecord::Migration
def change
add_column :documents, :uuid, :uuid, default: "uuid_generate_v4()", null: false
change_table :documents do |t|
t.remove :id
t.rename :uuid, :id
end
execute "ALTER TABLE documents ADD PRIMARY KEY (id);"
end
end
source

Rails automatically handles uuid, so you just need to change id to uuid, set the column type (uuid for PGSQL, string for MYSQL) and repopulate the table with the new uuid.
In doing this myself, I've only ever changed the id column to uuid for Rails to populate it automatically.
$ rails g migration ChangeID
#db/migrate/change_id______.rb
class ChangeId < ActiveRecord::Migration
def change
rename_column :documents, :id, :uuid
change_column :documents, :uuid, :uuid #-> will only work for PGQL, will have to make it string for MYSQL
end
end
$ rake db:migrate
This will rename your :id column to uuid, assigning the respective column type to it.
And, yes, I've used :uuid before...
--
A good ref:
http://labria.github.io/2013/04/28/rails-4-postgres-uuid-pk-guide/

Related

Difference between t.change and change_column in rails?

I am new in learning ruby on rails, I am confused when to use change_column and when to use t.change for migrations?
For example
class CreateProducts < ActiveRecord::Migration[7.0]
def change
create_table :products do |t|
t.string :name
t.text :description
t.timestamps
end
end
end
The def change method is part of all migrations. This method contains the changes that you want to apply during a given migration.
All your migrations will either have change method or an up and down method. If you define a change method like this:
class CreateProducts < ActiveRecord::Migration[7.0]
def change
create_table :products do |t|
t.string :name
t.text :description
t.timestamps
end
end
end
then when you apply this migration, a table will be created, and when you roll back this migration, Rails will try to generate a reverse of the migration. In this case, the reverse of create_table would be to drop the table.
Now suppose you already have this table created, but then you realize that you want to limit the length of the name field, then you can generate a migration to do that. This migration will use change_column method because you are now trying to change the definition of an existing column.
class LimitProductName < ActiveRecord::Migration[7.0]
def change
change_column :products, :name, :string, limit: 100
end
end

Rails—Migrate a table (with data) to have UUID instead of ID

I have a Project model, which has dependents, and there are already records in the db.
I want to change Project.id (it's primary key) to be a UUID, instead of just an incrementing integer.
How do I write a migration that will change the id, update existing records to have UUID, and update their references in other tables' foreign keys?
To migrate from default id to use uuid, try writing migration like this:
class ChangeProjectsPrimaryKey < ActiveRecord::Migration
def change
add_column :projects, :uuid, :uuid, default: "uuid_generate_v4()", null: false
change_table :projects do |t|
t.remove :id
t.rename :uuid, :id
end
execute "ALTER TABLE projects ADD PRIMARY KEY (id);"
end
end

generate a model with a string field as primary key

I want to create a User model inside my Ruby on Rails application.
I use following command:
rails generate model User email:string name:string role:string
It is possible to define the email as primary key with this command? Or I must modify the database migration file that I create with this command? And how?
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|
To add to #Simone Carletti's answer, you may need to use execute to set the primary key (if it's obscure). This would be especially true if you're modifying an existing table, which you're obviously not doing:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users, id: false do |t|
t.string :email, null: false
t.timestamps
end
execute "ALTER TABLE users ADD PRIMARY KEY (email);"
end
end
We use uuid's in some of our apps, and that's what we had to do (primary_key: :uuid didn't work)...
In Migration 6.0 or Rails 6
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users, id: false do |t|
t.string :email, null: false, primary_key: true
t.timestamps
end
end
end
In recent versions of ActiveRecord you can use --primary-key-type to specify the primary key type in the generate command:
rails generate model User email:string --primary-key-type=string
produces the migration:
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users, id: :string do |t|
t.string :email
t.timestamps
end
end
end
However, as far as I can tell there's still no way to change the name of the primary key column, except by hand.

How to migrate from using one foreign key to another in ActiveRecord?

Using Postgres as the backing store, I have a table which (at least for the time being) has both an integer primary key and a uuid with a unique index.
It looks something like this in my schema.rb (simplified for example):
create_table "regions", force: cascade do |t|
t.integer "region_id"
t.uuid "uuid", default: "uuid_generate_v4()"
t.string "name"
end
add_index "regions", ["uuid"], name "index_regions_on_uuid", unique: true, using :btree
I then have a table which has a reference to the integer id, something like this:
create_table "sites", force:cascade do
t.integer "site_id"
t.integer "region_id"
t.string "name"
end
What I want to do is to switch from region_id to uuid as the foreign key in the second table. How should I god about writing this migration?
Just create a migration, and inhale some SQL magic into it:
def up
# Create and fill in region_uuid column,
# joining records via still existing region_id column
add_column :sites, :region_uuid
if Site.reflect_on_association(:region).foreign_key == 'region_id'
# We won't use 'joins(:regions)' in case we will need
# to re-run migration later, when we already changed association
# code as suggested below. Specifying join manually instead.
Site.joins("INNER JOIN regions ON site.region_id = regions.id").update_all("region_uuid = regions.uuid")
end
drop_column :sites, :region_id
end
Then you just need to fix your association:
class Site < ActiveRecord::Base
belongs_to :region, primary_key: :uuid, foreign_key: :region_uuid
end
class Region < ActiveRecord::Base
has_many :sites, primary_key: :uuid, foreign_key: :region_uuid
end
From your comment, its seems that you want to modify the primary key referenced by the association, not the foreign key. You actually don't need a migration to do this. Instead, just specify the primary key on the association definitions in each model:
Class Region << ActiveRecord::Base
has_many :sites, primary_key: :uuid
end
Class Site << ActiveRecord::Base
belongs_to :region, primary_key: :uuid
end
The foreign key, since it follows rails convention of being named as the belongs_to relation with an appended "_id" (in this case, region_id), does not need to be specified here.
ETA: You will also need to ensure that the type of sites.region_id matches the type of regions.uuid, which I assume is uuid. I'm also going to assume that this field was previously indexed (under ActiveRecord convention) and that you still want it indexed. You can change all this in a migration like so:
def up
remove_index :sites, :region_id
change_column :sites, :region_id, :uuid
add_index :sites, :region_id
end
def down
remove_index :sites, :region_id
change_column :sites, :region_id, :integer
add_index :sites, :region_id
end

Rails reference a model with a non integer primary key

I have a table whose migration is this:
class CreateClient < ActiveRecord::Migration
def change
create_table :clients, :primary_key => :tag do |t|
t.string :name
end
change_column :clients, :tag, :string, limit: 4
end
end
so the primary key as you can see is that tag field which is a varchar(4), now I need to reference that field from another table.
I tried in the migration to create the reference in this way:
t.references :client, index: true
but the resulting table has a field called client_id which is int, should I pass any parameter to the references method so that it will create the field as varchar?
Thank you in advance
I believe you can use execute to force rails into setting the primary key in the way you want:
class CreateClient < ActiveRecord::Migration
def change
create_table :clients, :id => false do |t|
t.string :name
end
execute "ALTER TABLE clients ADD PRIMARY KEY (name);"
And then set do set_primary_key :name in your Client model

Resources