I have some issues. I'm new to RoR
I'm trying to create a join table using Rails migration. Documentation of this is here...
http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-create_join_table
When I do...
rails g migration CreateJoinTableUserOffer users offers
...it creates the following migration
class CreateJoinTableUserOffer < ActiveRecord::Migration
def change
create_join_table:users, :offers do |t|
t.index [:user_id, :offer_id]
t.index [:offer_id, :user_id]
end
end
end
and when I do...
rake db:migrate
it creates...
-- Table: offers_users
-- DROP TABLE offers_users;
CREATE TABLE offers_users
(
user_id integer NOT NULL,
offer_id integer NOT NULL
)
WITH (
OIDS=FALSE
);
ALTER TABLE offers_users
OWNER TO sudeepkaushik;
-- Index: index_offers_users_on_offer_id_and_user_id
-- DROP INDEX index_offers_users_on_offer_id_and_user_id;
CREATE INDEX index_offers_users_on_offer_id_and_user_id
ON offers_users
USING btree
(offer_id, user_id);
-- Index: index_offers_users_on_user_id_and_offer_id
-- DROP INDEX index_offers_users_on_user_id_and_offer_id;
CREATE INDEX index_offers_users_on_user_id_and_offer_id
ON offers_users
USING btree
(user_id, offer_id);
What I want to do is that I first of all want the table name to be users_offers instead of offers_users, for this you can specify the :table_name in the create_join_table migration. I'm not able to get the syntax of setting this option correctly. Need help here!
2nd, I noticed that this migration doesn't create the foreign keys that I would expect with the Users and Offers tables. Need your comments here also. Do I need to manually create the foreign keys myself?
You can define your join tables name with the table_name option.
create_join_table :users, :offers, table_name: :users_offers
There is also an option for setting column options called column_options, but I only got it to work for indexes and not for foreign keys.
create_join_table :users, :offers, column_options: { index: true }
This will create the desired indexes, but it ignores foreign_key: true. So you need to create them separately.
add_foreign_key :users_offers, :users
add_foreign_key :users_offers, :offers
In your models you need to add the relation like this:
# user.rb
has_and_belongs_to_many :offers, join_table: :users_offers
And:
# offers.rb
has_and_belongs_to_many :users, join_table: :users_offers
Related
I am trying to add a foreign key to via migration. It works as expected, but it automatically adds _id to the end (the column name I want to reference doesn't include _id). How can I make it reference the column name as I give it?
Here is the migration
class ChangeRefOnMemberPresentations < ActiveRecord::Migration[5.2]
def change
add_reference :member_presentations, 'employee_number', foreign_key: { to_table: :users }
end
end
Which results in both the reference column name and foreign key reference column being called employee_number_id in schema.rb
The following worked by defining everything manually, but seems messy. If there is a better migration answer, I'll be happy to accept.
def change
# column was added in another migration, but including for completness
add_column :member_presentations, :employee_number, :bigint
add_index :member_presentations, :employee_number, name: "index_member_presentations_on_employee_number"
add_foreign_key :member_presentations, :users, column: "employee_number"
end
(Rails is version 5.0.0, Ruby 2.3.0p0)
I want to create an association between my Users table and Cards table. I've added belongs_to :user to the Cards model, and has_many :cards to the Users model, and created a migration with:
class AddUserIdToCard < ActiveRecord::Migration[5.0]
def change
add_foreign_key :cards, :users, column: :user_id
end
end
When I run rake db:migrate, I receive the error:
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column "user_id" referenced in foreign key constraint does not exist
: ALTER TABLE "cards" ADD CONSTRAINT "fk_rails_8ef7749967"
FOREIGN KEY ("user_id")
REFERENCES "users" ("id")
Now I initially worked around this problem simply by adding add_column :cards, :user_id, :integer to the migration, but that doesn't really seem very tidy, and I'm worried about problems coming up later. Is there a better way to accomplish this?
You're setting a foreign key for cards table with the column user_id. But you haven't created a reference yet. Create a reference and then add foreign key to maintain referential integrity. Rollback and modify your migration with
1 class AddUserIdToCard < ActiveRecord::Migration[5.0]
2 def change
3 add_reference :cards, :users, index:true
4 add_foreign_key :cards, :users
5 end
6 end
Line 3 will create, in the cards table, a reference to id in the users table (by creating a user_id column in cards).
Line 4 will add a foreign key constraint to user_id at the database level.
For more, read Add a reference column migration in Rails 4
The answer provided is not accurate for Rails 5. Scroll to the add_reference bit of the docs for more, but in the case of the above question, you would use:
class AddUserIdToCard < ActiveRecord::Migration[5.0]
def change
add_reference :cards, :users, foreign_key: true
end
end
In rails 6, I believe this is now
def change
add_column :cards, :user_id, :integer, index: true
add_foreign_key :cards, :users
end
class AddUserIdToCard < ActiveRecord::Migration[5.2]
def change
add_foreign_key :cards, :users, column: :user_id, primary_key: :"id", on_delete: :cascade
end
end
Try this migration. I was facing the same problem then I fixed it.
Is there any difference between using t.references and executing SQL command to create foreign key relationship between products and category table as shown below? In other words, are the two different ways of doing the same thing or am I missing anything here?
class ExampleMigration < ActiveRecord::Migration
def up
create_table :products do |t|
t.references :category
end
#add a foreign key
execute <<-SQL
ALTER TABLE products
ADD CONSTRAINT fk_products_categories
FOREIGN KEY (category_id)
REFERENCES categories(id)
SQL
add_column :users, :home_page_url, :string
rename_column :users, :email, :email_address
end
def down
rename_column :users, :email_address, :email
remove_column :users, :home_page_url
execute <<-SQL
ALTER TABLE products
DROP FOREIGN KEY fk_products_categories
SQL
drop_table :products
end
end
They're not the same thing. Rails by default doesn't enforce foreign keys in the database. Instead, references when creating from the command line also creates a regular index, like this:
add_index :products, :category_id
Update:
Rails 5 actually does exactly the same thing now. So, to answer the original question: Nowadays, both are the same.
I found some thing intresting in this page.
http://railsforum.com/viewtopic.php?id=17318
From a comment :
Rails doesn't use foreign keys to perform his backend tasks. This
because some db like sqlite doesn't allow foreign keys on its tables.
So Rails doesn't provide an helper to build a foreign key
Also there is a gem foreigner for adding foreign keys to database table.
What creates the FOREIGN KEY constraint in Ruby on Rails 3?
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
So I have two rails models - user and role - each specified with a habtm and a join table of users_roles.
When I save from my user form, which has checkboxes for which roles to choose, rails is trying to manually insert a value into the id column of the join table. This doesn't make sense to me, as the id column should be set to auto_increment and so wouldn't need a value passed to it. Any time I try to save, I get a MySQL error.
Am I missing something?
Thanks.
if you are using the has_and_belongs_to_many association then you don't need any model for the join table and the join table should not be created with an id column, it has to contain only the user_id and role_id columns:
class CreateUsersRolesJoinTable < ActiveRecord::Migration
def self.up
create_table :users_roles, :id => false do |t|
t.integer :user_id
t.integer :role_id
end
end
def self.down
drop_table :users_roles
end
end
See also:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#M001836
http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association