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.
Related
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
It looks like the default :id is the integer, can I make it as a string?
Yes you can.
First run a migration:
create_table 'table' id: false, force: true do |t|
t.string 'id', null: false
end
specifying the type string for the id.
Then in your model:
class Table < ActiveRecord::Base
self.primary_key = :id
end
Which will basically explicitly indicate that the string id is the primary key of the object instance.
You should also consider looking up about uuid's in Rails.
Despite it being mainly a PostgreSQL piece of functionality, we've found it works well with MYSQL too:
You can set it up like this:
#app/models/post.rb
class Post < ActiveRecord::Base
before_create :set_uuid
private
def set_uuid
self.uuid = loop do
random_token = SecureRandom.hex(5)
break random_token unless self.class.exists? random_token
end
end
end
This can be accompanied - as pointed out by Cyzanfar - by replacing the id primary key with uuid. Rails 4 automatically supports uuid...
def change
create_column :posts, :uuid, :string
remove_column :posts, :id
rename_column :posts, :uuid, :id
execute "ALTER TABLE table ADD PRIMARY KEY (uuid);"
end
Some references:
Rails 4. Migrate table id to UUID
http://www.lshift.net/blog/2013/09/30/changing-the-primary-key-type-in-ruby-on-rails-models/
--
Because Rails 4 supports uuid out of the box, this should work for you.
As mentioned, we use uuid for some of our models (it allows us to maintain functionality whilst keeping records unique)
What is the proper way to create a table in rails via a migration in which the primary key is a string instead of an int?
I've tried setting primary_key as #oldergod suggested in the answer below but baz seems to get set to an int still:
class CreateFoos < ActiveRecord::Migration
def change
create_table :foos, primary_key: 'baz' do |t|
end
end
end
UPDATE
I've since tried
class CreateFoos < ActiveRecord::Migration
def change
create_table :foos, primary_key: false do |t|
t.string :baz
end
end
end
which gets me a little closer but still missing the PRIMARY index on the column. I've tried add_index :foos, :baz, type: :primary but this generates the following error:
SQLite3::SQLException: near "primary": syntax error: CREATE primary INDEX "index_foos_on_baz" ON "foos" ("baz")/Users/kyledecot/.rvm/gems/ruby-1.9.3-p392/gems/sqlite3-1.3.8/lib/sqlite3/database.rb:91:in `initialize'
It seems like this should work after looking at http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_index_options
What's different if it's a string? See the create table doc.
create_table :foos, primary_key: 'baz' do |t|
t.column :baz, :string
end
Also note that this just sets the primary key in the table. You
additionally need to configure the primary key in the model via
self.primary_key=. Models do NOT auto-detect the primary key from
their table definition.
I have two models restaurant and user that I want to perform a has_and_belongs_to_many relationship.
I have already gone into the model files and added the has_and_belongs_to_many :restaurants and has_and_belongs_to_many :users
I assume at this point I should be able to do something like with Rails 3:
rails generate migration ....
but everything I have tried seems to fail. I'm sure this is something really simple I'm new to rails so I'm still learning.
You need to add a separate join table with only a restaurant_id and user_id (no primary key), in alphabetical order.
First run your migrations, then edit the generated migration file.
Rails 3
rails g migration create_restaurants_users_table
Rails 4:
rails g migration create_restaurants_users
Rails 5
rails g migration CreateJoinTableRestaurantUser restaurants users
From the docs:
There is also a generator which will produce join tables if JoinTable
is part of the name:
Your migration file (note the :id => false; it's what prevents the creation of a primary key):
Rails 3
class CreateRestaurantsUsers < ActiveRecord::Migration
def self.up
create_table :restaurants_users, :id => false do |t|
t.references :restaurant
t.references :user
end
add_index :restaurants_users, [:restaurant_id, :user_id]
add_index :restaurants_users, :user_id
end
def self.down
drop_table :restaurants_users
end
end
Rails 4
class CreateRestaurantsUsers < ActiveRecord::Migration
def change
create_table :restaurants_users, id: false do |t|
t.belongs_to :restaurant
t.belongs_to :user
end
end
end
t.belongs_to will automatically create the necessary indices. def change will auto detect a forward or rollback migration, no need for up/down.
Rails 5
create_join_table :restaurants, :users do |t|
t.index [:restaurant_id, :user_id]
end
Note: There is also an option for a custom table name that can be passed as a parameter to create_join_table called table_name. From the docs
By default, the name of the join table comes from the union of the
first two arguments provided to create_join_table, in alphabetical
order. To customize the name of the table, provide a :table_name
option:
The answers here are quite dated. As of Rails 4.0.2, your migrations make use of create_join_table.
To create the migration, run:
rails g migration CreateJoinTableRestaurantsUsers restaurant user
This will generate the following:
class CreateJoinTableRestaurantsUsers < ActiveRecord::Migration
def change
create_join_table :restaurants, :users do |t|
# t.index [:restaurant_id, :user_id]
# t.index [:user_id, :restaurant_id]
end
end
end
If you want to index these columns, uncomment the respective lines and you're good to go!
When creating the join table, pay careful attention to the requirement that the two tables need to be listed in alphabetical order in the migration name/class. This can easily bite you if your model names are similar, e.g. "abc" and "abb". If you were to run
rails g migration create_abc_abb_table
Your relations will not work as expected. You must use
rails g migration create_abb_abc_table
instead.
For HABTM relationships, you need to create a join table. There is only join table and that table should not have an id column. Try this migration.
def self.up
create_table :restaurants_users, :id => false do |t|
t.integer :restaurant_id
t.integer :user_id
end
end
def self.down
drop_table :restaurants_users
end
You must check this relationship rails guide tutorials
Here is the Customer:
class CreateCustomer < ActiveRecord::Migration
def self.up
create_table :customers do |t|
t.column :email, :string, :null => false
end
end
def self.down
drop_table :customers
end
end
And this is the customer Info:
class CustomerInfo < ActiveRecord::Migration
def self.up
create_table :statuses do |t|
t.column :statuses, :string, :null => false
end
end
def self.down
drop_table :status
end
end
What I would like to do is the customer and customer Info have a one to one relationship. How can I do it in a new migration? thank you.
When you want a 1 to 1 in Rails, you have to decide which one of the models will store the foreign key. In your case, you probably want status to store the fk, so add an integer column called customer_id to the status table. Then you can add the has_one/belongs_to on Customer and Status. belongs_to always goes on the model with the foreign key.
Also I'm not sure if Rails will like you calling your table with the singular name, so you will probably have to do some extra work if you really want to call it 'status' instead of 'statuses'
You can try following thing in your next migration
add_column :customer_infos , :customer_id , :integer ,:references=>"customers" , :null=>:true
Then you can add the has_one/belongs_to on Customer and Cusomer_infos .
You can also execute an SQL statement.
statement = "ALTER TABLE users CHANGE id id SMALLINT( 5 ) UNSIGNED NOT NULL AUTO_INCREMENT" ActiveRecord::Base.connection.execute(statement)
you can entry manually in your migration
Note this is just an example. The final SQL statement syntax depends on the database.