I am trying to get a handle on how to use foreign keys in Rails,
$ rails g scaffold categories cat:string value:integer
$ rails db:migrate
Then create a new table with a foreign key connecting to the first table categories,
$ rails g scaffold subcategories subcats:string subcatsvalue:integer categories:references
$ rails db:migrate
Then I append /categories to the url and the form is there as expected and I can do all CRUD operations.
Then I append /subcategories to the url and try to add some data to the form such as,
Subcats: blah Subcatsvalue: 123 Categories: cat1
should this be the id of the category or the name of the category?
/RubyLearningApp/db/migrate/20200413195730_create_categories.rb
class CreateCategories < ActiveRecord::Migration[5.0]
def change
create_table :categories do |t|
t.string :cat
t.integer :value
t.timestamps
end
end
end
/RubyLearningApp/db/migrate/20200413200303_create_subcategories.rb
class CreateSubcategories < ActiveRecord::Migration[5.0]
def change
create_table :subcategories do |t|
t.string :subcats
t.integer :subcatsvalue
t.references :categories, foreign_key: true
t.timestamps
end
end
end
Is this correct way to set up a foreign key between tables?
When I fill in the Categories with 'cat1' I get the following error,
Schema.rb
ActiveRecord::Schema.define(version: 20200413200303) do
create_table "categories", force: :cascade do |t|
t.string "cat"
t.integer "value"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "subcategories", force: :cascade do |t|
t.string "subcats"
t.integer "subcatsvalue"
t.integer "categories_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["categories_id"], name: "index_subcategories_on_categories_id"
end
end
The model files:
category.rb
class Category < ApplicationRecord
end
subcategory.rb
class Subcategory < ApplicationRecord
belongs_to :categories
end
Any help would be greatly appreciated,
Thanks,
There are a few things wrong with your scaffolds that may be causing the problems. The correct way to generate a scaffold is to use a singular scaffold name:
rails g scaffold Category
and
rails g scaffold SubCategory
This will use Rails built in Inflector to pluralize the names where necessary.
When using references, you should also use the singular:
category:references
This is the Rails Way
and it will sort out most of the problems you are having. The other issue is if you want to add the category to the url, you should nest your routes:
resources :categories do
resources :sub_categories
end
This will allow you to use routes like
http://localhost:3000/categories/1/subcategories
and
http://localhost:3000/categories/1/subcategories/1
The first number (the one closest to the left) is the category id and can be access by using params[:category_id]
in the sub_categories_controller.rb
file. The second number (the one closest to the right) is the sub_category id and can be accessed by params[:id]
in the sub_categories_controller.rb
file.
Well, after spending two days stuck figuring out how to solve the foreign key issue in Rails 6+ - even though i read a lot of comments from SO which did not do much help. I finally found the solution.
Using add_reference
in your migration, you can easily solve this.
Let's pick it up from where you have model files untouched and Rails generated.
For your Category
Model, you should have:
class Category < ApplicationRecord
has_many :subcategories, foreign_key: :categories_id
end
And for your Subcategory
Model, you should have:
class SucCategory < ApplicationRecord
belongs_to :category. foreign_key: :categories_id
end
This creates an Association atrribute that tells rails that a Category
has many Subcategories
that can be identified in the categories
table by a foreign key found in the subcategories
table known as categories_id
Then in your console, now run the command rails generate migration AddSubcategoriesToCategories
to create a migration file. Within the generated migration file, be sure to have the change
method;
class AddSubcategoriesToCategories < ActiveRecord::Migration[6.0]
def change
add_references :categories, :categories, references: :subcategories, type: :integer, index: false
end
end
This would create a categories_id column in your categories
table and tells ActiveRecord to reference the values(s) from the subcategories
table, automatically making it a foreign key.
Funny enough, the reason why the option :categories
appears a second time is because ActiveRecord by default, looks for the column named id within the table from which the foreign key is taken - as it is the default index on creating tables. But as a different column with a different name is defined as the index, you will have to specify the name of the column (eg. keyname) in the add_reference
function to make ActiveRecord append the phrase _id to what you just defined as the column name and find that column - now named 'keyname_id', else you'll receive errors that specify that the column 'id' referenced in foreign key constraint does not exist or if you specify the full column name as 'keyname_id' in your add_reference
function, you'll receive errors that specify that the column 'keyname_id' referenced in foreign key constraint does not exit
So in this case the second :categories
in the function is the first part of the name of the column to which ActiveRecord appends the remaining part '_id' to become :categories_id
.
Drawback: All your foreign keys would then have to be snakecased as 'whateverkeyname_id' in your tables
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.