Rails has_and_belongs_to_many migration - ruby-on-rails

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

Related

Rails create join table index name is too long

So I'm trying to create a join table between the tables users and looking_for_options.
This is my migration file:
class CreateJoinTableOptionsUsers < ActiveRecord::Migration[5.0]
def change
create_join_table :looking_for_options, :users do |t|
t.index [:looking_for_option_id, :user_id]
t.index [:user_id, :looking_for_option_id]
end
end
end
But I'm getting this error:
Index name 'index_looking_for_options_users_on_looking_for_option_id_and_user_id' on table 'looking_for_options_users' is too long; the lim
it is 64 characters
Knowing that for a join table, rails convention is Table_A_Name_Table_B_Name and its columns follow a similar convention Table_A_id and Table_B_id.
How do I specify a shorter column name for the joint table so it doesn't break rails many-to-many associations?
Update:
I found that I can give just the index a different name instead. But will rails's many-to-many association actually utilize it?
class CreateJoinTableOptionsUsers < ActiveRecord::Migration[5.0]
def change
create_join_table :looking_for_options, :users do |t|
t.index [:looking_for_option_id, :user_id], name: 'option_user'
t.index [:user_id, :looking_for_option_id], name: 'user_option'
end
end
end
... will rails's many-to-many association actually utilize it?
The choice of whether to use the index or not is made by the database optimiser, and is not affected by Rails. You can name it what you like, within the constraints imposed by the database.

Rationale of Rails 5 Join Table Generated Defaults

The Rails 5 command rails g migration create_foo_bar_join_table generates the following migration:
class CreateFooBarJoinTable < ActiveRecord::Migration[5.0]
def change
create_join_table :foos, :bars do |t|
# t.index [:foo_id, :bar_id]
# t.index [:bar_id, :foo_id]
end
end
end
Why does the generator stub out two (bi-directional) indices with composite keys? Also why are they commented out? I'm confused by this and can't find any clear explanation for having these suggested defaults.
Are the above indices more lookup efficient than the ones below?
...
create_join_table :foos, :bars do |t|
t.index :foo_id
t.index :bar_id
end
...
Stumbled on the exact answer reading the docs on has_and_belongs_to_many:
It’s also a good idea to add indexes to each of those columns to speed
up the joins process. However, in MySQL it is advised to add a
compound index for both of the columns as MySQL only uses one index
per table during the lookup.
https://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many

Migrate postgresql database to uuid rails 3.1

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.

adding a new has_many relationship to an existing model

I would like to add a has_many relationship to two existing tables/models in my app & I'm not too sure how to di it?
When I did this before with a new model the rails generate command handled everything for me, with just rails generate model Photo image:string hikingtrail:references it created the below migration
class CreatePhotos < ActiveRecord::Migration
def change
create_table :photos do |t|
t.string :image
t.references :hikingtrail
t.timestamps
end
add_index :photos, :hikingtrail_id
end
end
Now I would like set up a relationship between users & photos with each user has_many :photos.
When I generate a migration to achieve this it does not include the add_index :photos, :user_id, is this something I should be doing manually or are the below steps enough for setting up this relationship in my database?
rails g migration AddUserIdToPhotos user_id:integer
which creates...
class AddUserIdToPhotos < ActiveRecord::Migration
def change
add_column :photos, :user_id, :integer
end
end
& then run...
rake db:migrate
It is enough to set up your relationship. You can add a index to improve the speed of your record searching. In fact some recommend to put a index to all the foreign keys. But don't worry about this now, i guess you are not going to have that many records to use a index.
If you have already migrated everything and want to add a index make do:
rails g migration AddIndexToUserIdToPhotos
and inside add the index column:
class AddUserIdToPhotos < ActiveRecord::Migration
def change
add_index :photos, :user_id
end
end

Rails 3 migrations: Adding reference column?

If I create a new rails 3 migration with (for example)
rails g migration tester title:tester user:references
, everything works fine...however if I add a column with something along the lines of:
rails g migration add_user_to_tester user:references
the reference field is not recognised. In short, the question is: how do I add a referencing column to a rails migration from the command line?
If you are using the Rails 4.x you can now generate migrations with references, like this:
rails generate migration AddUserRefToProducts user:references
like you can see on rails guides
EDIT: This is an outdated answer and should not be applied for Rails 4.x+
You don't need to add references when you can use an integer id to your referenced class.
I'd say the advantage of using references instead of a plain integer is that the model will be predefined with belongs_to and since the model is already created and will not be affected when you migrate something existing, the purpose is kind of lost.
So I would do like this instead:
rails g migration add_user_id_to_tester user_id:integer
And then manually add belongs_to :user in the Tester model
Please note that you will most likely need an index on that column too.
class AddUserReferenceToTester < ActiveRecord::Migration
def change
add_column :testers, :user_id, :integer
add_index :testers, :user_id
end
end
With the two previous steps stated above, you're still missing the foreign key constraint. This should work:
class AddUserReferenceToTester < ActiveRecord::Migration
def change
add_column :testers, :user_id, :integer, references: :users
end
end
You can use references in a change migration. This is valid Rails 3.2.13 code:
class AddUserToTester < ActiveRecord::Migration
def change
change_table :testers do |t|
t.references :user, index: true
end
end
def down
change_table :testers do |t|
t.remove :user_id
end
end
end
c.f.: http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/change_table
Running rails g migration AddUserRefToSponsors user:references will generate the following migration:
def change
add_reference :sponsors, :user, index: true
end
When adding a column you need to make that column an integer and if possible stick with rails conventions. So for your case I am assuming you already have a Tester and User models, and testers and users tables.
To add the foreign key you need to create an integer column with the name user_id (convention):
add_column :tester, :user_id, :integer
Then add a belongs_to to the tester model:
class Tester < ActiveRecord::Base
belongs_to :user
end
And you might also want to add an index for the foreign key (this is something the references already does for you):
add_index :tester, :user_id
That will do the trick:
rails g migration add_user_to_tester user_id:integer:index
You can add references to your model through command line in the following manner:
rails g migration add_column_to_tester user_id:integer
This will generate a migration file like :
class AddColumnToTesters < ActiveRecord::Migration
def change
add_column :testers, :user_id, :integer
end
end
This works fine every time i use it..
For Rails 4
The generator accepts column type as references (also available as belongs_to).
This migration will create a user_id column and appropriate index:
$ rails g migration AddUserRefToProducts user:references
generates:
class AddUserRefToProducts < ActiveRecord::Migration
def change
add_reference :products, :user, index: true
end
end
http://guides.rubyonrails.org/active_record_migrations.html#creating-a-standalone-migration
For Rails 3
Helper is called references (also available as belongs_to).
This migration will create a category_id column of the appropriate type. Note that you pass the model name, not the column name. Active Record adds the _id for you.
change_table :products do |t|
t.references :category
end
If you have polymorphic belongs_to associations then references will add both of the columns required:
change_table :products do |t|
t.references :attachment, :polymorphic => {:default => 'Photo'}
end
Will add an attachment_id column and a string attachment_type column with a default value of Photo.
http://guides.rubyonrails.org/v3.2.21/migrations.html#creating-a-standalone-migration

Resources