简体   繁体   中英

Ruby on Rails Foreign Keys Issue

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM