rails nested resource unknown attribute error - ruby-on-rails

I have a Contract and a Task_Order model. I keep getting an unknown attribute error for contract_id Each Contract has many Task Orders. I have read other nested models unknown attribute error questions but they haven't been able to help me. Please keep in mind I am pretty new to Rails and would greatly appreciate any help I can get. I am using Rails 4.0
Contract Model:
has_many :task_orders
Contract schema:
create_table "contracts", force: true do |t|
t.string "contractId"
t.string "contractName"
end
Task Order Model:
belongs_to :contracts
Task Order Schema:
create_table "task_orders", force: true do |t|
t.string "contract_Id"
t.string "task_orderId"
t.string "task_orderName"
end
When I click Show Contract, I get the error:
unknown attribute: contract_id
This is the line that gets highlighted:
<%= form_for([#contract, #contract.task_orders.new]) do |f| %>
I can tell that Rails is trying to print out contract_id, which is not in my Contract model... so how can I get it to print out contractId instead - which is in my Contract model?
Thanks!!

Task Order Model should have this line belongs_to contract
belongs_to association should be declared as a singular of corresponding model
Also there should be contract_id column within task_orders table.
Diagram below explains default behavior of belongs_to in Rails

Something you need to be aware of is the foreign_key of Rails (and relational databases in general):
Foreign Key
Rails' standard foreign_key is to use snake_case (contract_id), however, you can use non-conventional foreign_keys like this:
#app/models/order.rb
belongs_to :contract, foreign_key: "contract_Id"
#schema SHOULD be:
create_table "orders", force: true do |t|
t.integer "contract_id" #-> should
t.string "contract_Id" #-> your current
end
Primary Key
create_table "contracts", force: true do |t|
t.string "contractId" #-> don't need
t.string "contractName" #-> your current
t.string "name" #-> should be
end
Your primary_key is almost always going to be the id column. You should remove your contractId column from the contracts db!
Task Orders
You'll need to do this:
#app/models/order.rb
belongs_to :contracts
has_many :task_orders
You'll then need another model at app/models/task_order.rb
Form
Your form is showing the error. This is because you're trying to create an ActiveRecord in the view itself. You'll be much better using the standard accepts_nested_attributes_for method of passing nested model data through a form:
#app/models/contract.rb
def new
#contract = Contract.new
#contract.task_orders.build
end
#app/views/contracts/new.html.erb
<%= form_for #contract do |f| %>

Firstly,use singular names for belongs_to
Class TaskOrder < ActiveRecord::Base
belongs_to :contract
end
Secondly,try changing your contract_Id in your task_orders table to contract_id.
Rails by default look for model_name_id(in your case contract_id) foreign key unless if any of the custom foreign keys defined in your model.
And finally,specify the data type integer for default foreign key.In your case it should be t.integer contract_id
However if you want contract_Id as foreign key,you should define it as custom foreign key in the Contract model itself like this
Class Contract < ActiveRecord:Base
has_many :task_orders,:foreign_key => "contract_Id"
end

Related

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 S.O 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

How to set foreign key in Rails

I Have a model named employee. The following is my migration file.
class CreateEmployees < ActiveRecord::Migration
def change
create_table :employees, id: false do |t|
t.string :name
t.string :password
t.string :role
t.primary_key :name
end
end
end
Now, I want to create a model named "teamplayer" with the columns as 'name' which needs to refers 'name' column in employee model. And 'tl' column
which is independent to this model. The following is my "teamplayer" migration file.
class CreateTeamplayers < ActiveRecord::Migration
def change
create_table :teamplayers, :id false do |t|
t.string :tl
t.string :name
end
end
end
In the above file, how to reference 'name' column to the model employee? So how to achieve foreign key in rails.
I think you want to look into Active Record Associations (http://guides.rubyonrails.org/association_basics.html)
I know you've asked to create a foreign key on name but unless you plan to ensure that name is unique, then this is possibly not the best plan (depending on the actual relationship you are trying to model - one to many / one to one etc).
I would be tempted to set up the foreign key relationship on employees.id. To do this, you can use the has_many and belongs_to associations.
You could change your teamplayers migration as follows:
class CreateTeamplayers < ActiveRecord::Migration
def change
create_table :teamplayers, :id false do |t|
t.belongs_to :employee
t.string :tl
end
end
end
Then in your Employee model, you can add the has_many side of things:
class Employee < ActiveRecord::Base
has_many :teamplayers
end
You can still easily get the Employee name given a Team Player record with a simple join.
Edit - to get the Employee, you can do something like this, assuming #tis a teamplayer instance:
#t.employee.name
(the code is untested and from memory so....)
You can do it in the teamplayer model, you just need to add index in your migration
class CreateTeamplayers < ActiveRecord::Migration
def change
create_table :teamplayers, :id false do |t|
t.string :tl
t.string :name
end
add_index :teamplayers, :name
end
end
You can set name as Primary key inside the employee model like this
class Employee < ActiveRecord::Base
self.primary_key = "name"
has_many :teamplayers
end
Now inside the model Teamplayer you can set the foreign key
class Teamplayer < ActiveRecord::Base
belongs_to :employee, foreign_key: 'name'
end
This should reference 'name' to employee model

Relations not working as expected

This will be fairly quick and easy for most of you...I have a table called types, and another called projects. A project can only have one type, but a type can have many projects. For instance a community garden project and a playground project can both have the type of 'greenspace'. So I have set up a has_many association. In my types model I have this:
has_many :projects
and in my projects model I don't have anything (I previously had has_one in it but upon looking at the docs it seemed incorrect). In the projects#show view I would like the name of the type to display. The parks project's view should say 'greenspace'. but I am getting the error
undefined method `type' for #<Project:0x007ffdd14fcde8>
I am trying to access that name using:
<h3>Type: <%= #project.type.project_type %> </h3>
i have also tried:
<h3>Type: <%= #project.type_id.project_type %> </h3>
but of course type_id gives a number, and there is no project_type for a number. project_type being the name of the column which holds the string data 'greenspace'. Am I accessing it wrong? Or have I set it up incorrectly?
Also in my schema, projects looks like this:
create_table "projects", force: :cascade do |t|
t.string "type_id"
t.text "description"
t.integer "money_needed"
t.integer "money_raised"
t.float "interest_offered"
t.datetime "end_date"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
t.text "url"
end
Project can belong_to both. Like this
#app/models/project.rb
class Project < ActiveRecord::Base
belongs_to :type
belongs_to :user
#...
end
#app/models/user.rb
class User < ActiveRecord::Base
has_many :projects
#...
end
#app/models/type.rb
class Type < ActiveRecord::Base
has_many :projects
#...
end
In the Project model you should state:
belongs_to => :type
In general, for most associations there is going to be an inverse. Not always, as you might have multiple associations in Type for Project. For example as well as your current has_many :projects, you might have others to return only projects that are unfinished, and such an association would not need an inverse.
Bear in mind that when you state: #project.type Rails is going to look for a method on #project. The association is what provides this method, and effectively the result is then the Type object that is referenced by the Project. It's important to realise that #project.type only returns a Type because the association tells it to -- the magic does not extent to just inferring that that is what is wanted.

Custom foreign_key in model gives PG::Error column does not exist - Rails

I have a VideoCollection model that will contain many records from another model (called VideoWork), using the has_many relationship. The VideoCollection model inherits from the Collection model using single table inheritance, while the VideoWork model inherits from the Work model.
I'm having a problem when I try to call up the video_works that belong to a video_collection.
In my video_collection#show action, I use the following to try to display a collection's works:
def show
#video_collection = VideoCollection.find(params[:id])
#collections = #video_collection.children
#works = #video_collection.video_works
end
But when I try to use #works in the show view, I get the following:
PG::Error: ERROR: column works.video_collection_id does not exist
SELECT "works".* FROM "works" WHERE "works"."type" IN ('VideoWork') AND "works"."video_collection_id" = $1
##(Error occurs in the line that contains <% #works.each do |work| %>)
My model files:
#----app/models/video_collection.rb----
class VideoCollection < Collection
has_many :video_works
end
#----app/models/video_work.rb----
class VideoWork < Work
belongs_to :folder, class_name: "VideoCollection", foreign_key: "folder_id"
end
The "parent" models:
#----app/models/collection.rb - (VideoCollection inherits from this)
class Collection < ActiveRecord::Base
end
#----app/models/work.rb - (VideoWork inherits from this)
class Work < ActiveRecord::Base
end
The Schema file:
#----db/schema.rb----
create_table "works", force: true do |t|
t.string "header"
t.string "description"
t.string "type"
t.string "folder_id"
end
create_table "collections", force: true do |t|
t.string "type"
t.datetime "created_at"
t.datetime "updated_at"
t.text "ancestry"
t.string "name"
t.string "tile_image_link"
end
My Question
I assume that since I have a folder_id column in the works table that I should be able to set up the belongs_to relationship properly, but it seems that Rails still wants me to have a video_collection_id column instead. I would prefer not use something specific like video_collection_id as a foreign key in the works table since I need to set up other relationships (e.g.: photo_collection has_many photo_works, etc).
What am I doing wrong here?
I don't really use has_many and belongs_to with different foreign keys than the standard, but according to the docs I would do this:
class VideoCollection < Collection
has_many :video_works, foreign_key: "folder_id"
end
class VideoWork < Work
belongs_to :folder, class_name: "VideoCollection", foreign_key: "folder_id"
end
Your Pg error says that the association is looking for 'video_collection_id' instead of 'folder_id'
Guides (chapter 4.3.2.5)

How to create a rails habtm that deletes/destroys without error?

I created a simple example as a sanity check and still can not seem to destroy an item on either side of a has_and_belongs_to_many relationship in rails.
Whenever I try to delete an object from either table, I get the dreaded NameError / "uninitialized constant" error message.
To demonstrate, I created a sample rails app with a Boy class and Dog class. I used the basic scaffold for each and created a linking table called boys_dogs. I then added a simple before_save routine to create a new 'dog' any time a boy was created and establish a relationship, just to get things setup easily.
dog.rb
class Dog < ActiveRecord::Base
has_and_belongs_to_many :Boys
end
boy.rb
class Boy < ActiveRecord::Base
has_and_belongs_to_many :Dogs
def before_save
self.Dogs.build( :name => "Rover" )
end
end
schema.rb
ActiveRecord::Schema.define(:version => 20100118034401) do
create_table "boys", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "boys_dogs", :id => false, :force => true do |t|
t.integer "boy_id"
t.integer "dog_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "dogs", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
end
I've seen lots of posts here and elsewhere about similar problems, but the solutions are normally using belongs_to and the plural/singular class names being confused. I don't think that is the case here, but I tried switching the habtm statement to use the singular name just to see if it helped (with no luck). I seem to be missing something simple here.
The actual error message is:
NameError in BoysController#destroy
uninitialized constant Boy::Dogs
The trace looks like:
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.4/lib/active_support/dependencies.rb:105:in const_missing'
(eval):3:indestroy_without_callbacks'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/callbacks.rb:337:in destroy_without_transactions'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/transactions.rb:229:insend'
...
Thanks.
I don't see your destroy callback, but I do see a couple of problems. First, your associations need to be lowercase. So dog.rb should be:
class Dog < ActiveRecord::Base
has_and_belongs_to_many :boys
end
and boy.rb should be:
class Boy < ActiveRecord::Base
has_and_belongs_to_many :dogs
def before_save
self.dogs.build( :name => "Rover" )
end
end
Second, I believe you want to use self.dogs.create instead of self.dogs.build above, since build won't actually save the new dog object.
The accepted answer here solved my problem, only to create another one.
Here are my model objects:
class Complex < ActiveRecord::Base
set_table_name "Complexes"
set_primary_key "ComplexID"
has_and_belongs_to_many :amenities
end
class Amenity < ActiveRecord::Base
set_table_name "Amenities"
set_primary_key "AmenityID"
end
Rails uses the name of the association as the table name when creating the select query. My application runs on Unix against a legacy MySQL database and my table names are case-sensitive and don't conform to Rails conventions. Whenever my app actually tried to load the association, I would get an exception that MySQL couldn't find table amenities:
SELECT * FROM `amenities`
INNER JOIN `ComplexAmenities` ON `amenities`.AmenityID = `ComplexAmenities`.AmenityID
WHERE (`ComplexAmenities`.ComplexID = 147 )
I searched and searched and could not find a way to tell Rails to use the correct case for the table name. Out of desperation, I tried passing a :table_name option to habtm and it worked. My new Complex model looks like this:
class Complex < ActiveRecord::Base
set_table_name "Complexes"
set_primary_key "ComplexID"
has_and_belongs_to_many :amenities, :table_name => 'Amenities'
end
This works under Rails 2.3.5.
This option is not mentioned in the Ruby on Rails docs.

Resources