generate a model with a string field as primary key - ruby-on-rails

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.

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

Changing model id to uuid in Rails using uuid-ossp

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/

Why directly changing a migrate file does not change the schema file?

My current migrate file is
class CreateMovies < ActiveRecord::Migration
def up
create_table :movies, :force => true do |t|
t.string :title
t.string :rating
t.text :description
t.datetime :release_date
# Add fields that let Rails automatically keep track
# of when movies are added or modified:
t.timestamps
end
end
def down
drop_table :movies
end
end
I try to change release_date type to integer. So I directly change the file to
class CreateMovies < ActiveRecord::Migration
def up
create_table :movies, :force => true do |t|
t.string :title
t.string :rating
t.text :description
t.integer :release_date
# Add fields that let Rails automatically keep track
# of when movies are added or modified:
t.timestamps
end
end
def down
drop_table :movies
end
end
Please pay attention, the release_date type has been changed. But after I run
bundle exec rake db:migrate
It still produce the same schema file as before. I am so confused.
It's probably because you've already run your migration. So before you want to change it, you should rollback it first:
bundle exec rake db:rollback
then you should modify it and run again:
bundle exec rake db:migrate
As an alternative to dropping and upping the migration, you could make a new migration to change the column type.
class ChangeMoviesReleaseTypeToInteger < ActiveRecord::Migration
def up
change_column :movies, :release_date, :integer
end
def down
change_column :movies, :release_date, :datetime
end
end
Just as a side note, release_date is a confusing name for an integer field - most people would expect it to be a datetime as you had originally.
down will remove the table
rake db:migrate:down VERSION=file_name(exclude extension)
up will create with new changes
rake db:migrate:up VERSION=file_name(exclude extension)

rails generate migration add_index_to... does not put the actual index in the migration file

I created a users table via "rails generate model User name:string email:string ..." the migration file was created as well.
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.string :email
t.timestamps
end
end
end
Now I want to add an index to the email column "following the tutorial" I've done this successfully the first time through using sqlite3. Second time through im using MySql (mysql2). Again created the table fine with generate model.. When I run the following:
rails generate migration add_index_to_users_email
the process ends with no error message and creates the migration file as shown below, but there is no setting of any index..
class AddIndexToUsersEmail < ActiveRecord::Migration
def change
end
end
Im expecting to see add_index :users, :email, unique: true in there ... Anybody have any idea's.. searched other threads to no avail.. running rails 4, mysql 5.6 ruby 1.9.3 my schema that was created after initil db:migrate is:
ActiveRecord::Schema.define(version: 20131024161033) do
create_table "users", force: true do |t|
t.string "name"
t.string "email"
t.string "city"
t.string "state"
t.string "zip"
t.string "mobile_phone"
t.string "mobile_phone_type"
t.date "birth_date"
t.string "user_type"
t.string "ss_num"
t.boolean "agree_to_terms"
t.datetime "created_at"
t.datetime "updated_at"
end
end
via http://guides.rubyonrails.org/migrations.html
If you'd like to add an index on the new column, you can do that as
well:
$ rails generate migration AddPartNumberToProducts
part_number:string:index
your generator
rails generate migration add_index_to_users_email
simply creates an empty migration file and did not describe a index
so this would be more appropriate...
rails generate migration AddIndexToUsers email:string:index
should give you
class AddIndexToUsers < ActiveRecord::Migration
def change
add_index :users, :email
end
end
Nguyen You - EDIT
This command [Rails 5.2.3]
rails generate migration AddIndexToUsers email:string:index
actually will give you
class AddIndexToUsers < ActiveRecord::Migration[5.2]
def change
add_column :users, :email, :string
add_index :users, :email
end
end
not only add_index but also add_column to the users table.
rails generate migration AddIndexToUsers email:string:index
if you already have column it just add index, like:
class AddIndexToUsers < ActiveRecord::Migration
def change
add_index :users, :email
end
end
if you create new column (you haven't got column in database yet), it returns:
class AddIndexToUsers < ActiveRecord::Migration
def change
add_column :user, :email, :string
add_index :users, :email
end
end
From the http://railstutorial.ru/chapters/4_0/modeling-users#code-email_uniqueness_index.
The email uniqueness migration is not pre-defined, so we need to fill in its contents with this by ourself " add_index :users, :email, unique: true " .
The result will be:
class AddIndexToUsersEmail < ActiveRecord::Migration
def change
add_index :users, :email, unique: true
end
end

Resources