My many to many associations seem not to be working.
An example is this: where TasksTestMethods is the JoinTable
TasksTestMethods.all
TasksTestMethods Load (0.5ms) SELECT "tasks_test_methods".* FROM "tasks_test_methods" LIMIT $1 [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<TasksTestMethods task_id: 2, test_method_id: 1>]>
The join table in schema.rb looks like this
create_table "tasks_test_methods", id: false, force: :cascade do |t|
t.bigint "task_id", null: false
t.bigint "test_method_id", null: false
t.index ["task_id", "test_method_id"], name: "index_tasks_test_methods_on_task_id_and_test_method_id"
t.index ["test_method_id", "task_id"], name: "index_tasks_test_methods_on_test_method_id_and_task_id"
end
Now when I want to query for TestMethods for task with id of 2 as seen above.
task = Task.last
Task Load (0.9ms) SELECT "tasks".* FROM "tasks" ORDER BY "tasks"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<Task id: 2, name: "", description: "", start_date: "2018-05-15", end_date: nil, order_number: "", material_type: nil, client_id: 1, drawing_number: nil, task_status_field: nil, invoice_number: nil, offshore_flag: 0, client_ref: "", contact_person: "", contact_person_phone: "", contact_person_email: "", testsite: "", information_to_operator: nil, client_online_order: nil, start_date_asap: nil, thickness: nil, testextent: nil, groove: nil, material_quality: nil, class_society: nil, welder_id: nil, project_id: nil, operator_id: nil, task_manager_id: 1, department_id: 5, created_at: "2018-05-14 22:13:08", updated_at: "2018-05-14 22:18:30", language_id: 2>
I get the following error when I run it like this
task.test_methods
Traceback (most recent call last):
1: from (irb):6
NameError (uninitialized constant Task::TasksTestMethod)
Same when I try for TestMethod
test_method = TestMethod.last
TestMethod Load (0.4ms) SELECT "test_methods".* FROM "test_methods" ORDER BY "test_methods"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<TestMethod id: 1, name: "Visual Testing (VT)", code: nil, description: nil, category_id: 1, procedure_id: 1, created_at: "2018-05-15 07:06:51", updated_at: "2018-05-15 07:06:51">
I get this error:
test_method.tasks
Traceback (most recent call last):
1: from (irb):9
NameError (uninitialized constant TestMethod::TasksTestMethod)
I'm getting this same error for all JoinTables in my app.
What I'm I doing wrong? And my models are as follows:
class Task < ApplicationRecord
has_many :tasks_test_methods, :dependent => :destroy
has_many :test_methods, :through => :tasks_test_methods
end
class TestMethod < ApplicationRecord
belongs_to :category
belongs_to :procedure
has_many :tasks_test_methods
has_many :tasks, :through => :tasks_test_methods
end
class TasksTestMethods < ApplicationRecord
belongs_to :task
belongs_to :test_method
end
I also tried to rename the model name from TasksTestMethods to TasksTestMethod based on this error NameError (uninitialized constant Task::TasksTestMethod) but that didn't help.
Is there anything I'm missing?
Name your rb file containing this class properly, i.e. tasks_test_method.rb.
Related
Following up on the question : Want to find records with no associated records in Rails
I am wondering how I can get all the NON orphan records returned as an AssociationRelation instead of an Array. When trying to subtract the total records of the table from the rails 6 .missing ones, the result is correct, but it's in the form of an array.
Here is a console example :
p = ProductResearch.first
(Product.all - p.products.where.missing(:keywords)).class
=> Array
How do I get the association ?
( With the help of #max below I found a query, without missing, that returns the expected result as an association. It's like :
irb(main):206:0> p.products.includes(:keywords).where.not(keywords: { id: nil }).class
=> Product::ActiveRecord_AssociationRelation
and it does return the non orphan ones only.
Given:
class Post < ApplicationRecord
has_many :comments
end
class Comment < ApplicationRecord
belongs_to :post
end
class CreateComments < ActiveRecord::Migration[6.0]
def change
create_table :comments do |t|
# Referential integrity is for wusses! YOLO!
t.belongs_to :post, null: true, foreign_key: false
t.timestamps
end
end
end
p1 = Post.create!(title: 'Foo')
3.times { p1.comments.create! }
p2 = Post.create!(title: 'Bar')
3.times { p2.comments.create! }
p2.destroy! # orphans the comments
If you do an INNER JOIN on posts you will only get rows with at least one match in the join table:
irb(main):014:0> Comment.joins(:post)
Comment Load (0.3ms) SELECT "comments".* FROM "comments" INNER JOIN "posts" ON "posts"."id" = "comments"."post_id" LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Comment id: 1, post_id: 1, created_at: "2021-05-11 08:59:04", updated_at: "2021-05-11 08:59:04">, #<Comment id: 2, post_id: 1, created_at: "2021-05-11 08:59:04", updated_at: "2021-05-11 08:59:04">, #<Comment id: 3, post_id: 1, created_at: "2021-05-11 08:59:04", updated_at: "2021-05-11 08:59:04">]>
This gives you the "non-orphaned" posts.
The opposite is of course an OUTER JOIN:
irb(main):016:0> Comment.left_joins(:post).where(posts: { id: nil })
Comment Load (0.3ms) SELECT "comments".* FROM "comments" LEFT OUTER JOIN "posts" ON "posts"."id" = "comments"."post_id" WHERE "posts"."id" IS NULL LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Comment id: 4, post_id: 2, created_at: "2021-05-11 08:59:26", updated_at: "2021-05-11 08:59:26">, #<Comment id: 5, post_id: 2, created_at: "2021-05-11 08:59:26", updated_at: "2021-05-11 08:59:26">, #<Comment id: 6, post_id: 2, created_at: "2021-05-11 08:59:26", updated_at: "2021-05-11 08:59:26">]>
Rails 6.1 added the .missing query method which is a shortcut for the above query:
Comment.where.missing(:post)
I am trying to implement a three level deep association using self referencing.
Cat1
Sub1
SubSub1
SubSub2
Sub2
Cat2
Sub1
Cat3
Sub1
Sub2
SubSub1
I am able to get the child category of a category by this relation:
class Category < ActiveRecord::Base
has_many :sub_categories, class_name: "Category", foreign_key: :parent_id
end
This is fine when i have only two level deep category. For three level deep association using self referencing i tried using this relation, but failed to get the desired output.
class Category < ActiveRecord::Base
belongs_to :parent_category, class_name: "Category"
has_many :sub_categories, class_name: "Category", foreign_key: :parent_id
end
here is what i get using this association.query fired on Category.find(3).parent_category is wrong.
2.0.0-p648 :012 > Category.find(2)
Category Load (1.2ms) SELECT `categories`.* FROM `categories` WHERE `categories`.`id` = 2 LIMIT 1
=> #<Category id: 2, title: "Suit", description: "sffdsfsxcx ssdfvvs", seo_name: "sfsdf", parent_id: nil, hoe_page: nil, status: true, sequence: "1", banner_image_file_name: nil, banner_image_content_type: nil, banner_image_file_size: nil, banner_image_updated_at: nil, image_file_name: nil, image_content_type: nil, image_file_size: nil, image_updated_at: nil, home_description: "adhkadaa", home_page: true, long_description: "sdfsddfffssssde", created_at: "2018-04-09 07:42:55", updated_at: "2018-04-09 07:42:55">
2.0.0-p648 :013 > Category.find(3)
Category Load (1.1ms) SELECT `categories`.* FROM `categories` WHERE `categories`.`id` = 3 LIMIT 1
=> #<Category id: 3, title: "a", description: "aaa", seo_name: "a", parent_id: 2, hoe_page: nil, status: true, sequence: "1", banner_image_file_name: nil, banner_image_content_type: nil, banner_image_file_size: nil, banner_image_updated_at: nil, image_file_name: nil, image_content_type: nil, image_file_size: nil, image_updated_at: nil, home_description: "aaa", home_page: true, long_description: "aaa", created_at: "2018-04-09 09:44:11", updated_at: "2018-04-09 09:44:11">
2.0.0-p648 :014 > Category.find(3).parent_category
Category Load (1.1ms) SELECT `categories`.* FROM `categories` WHERE `categories`.`id` = 3 LIMIT 1
=> nil
Please help me here by making me understand what would be the perfect association for my purpose. Please dont give me gem name like "Ancestry" or "awesome_nested_set", I need pure rails associations.
Try this:
belongs_to :parent_category, foreign_key: :parent_id, class_name: 'Category'
has_many :sub_categories, foreign_key: :parent_id, class_name: 'Category'
I'm having a hard time with Rails and nested attributes and would really appreciate some help.
Here is the output from my console session where I was attempting to get the updated values to save but as you can see, they don't seem to take on the next line when I perform the find again:
irb(main):070:0* e = Equipment.find(26)
Equipment Load (0.5ms) SELECT "equipment".* FROM "equipment" WHERE "equipment"."id" = $1 LIMIT 1 [["id", 26]]
=> #<Equipment id: 26, name: "fdsfsdsdfsd2", created_at: "2015-11-02 15:26:43", updated_at: "2015-11-02 16:38:55", site_id: 57, type_id: 3>
irb(main):071:0> e.update({"name"=>"fdsfsdsdfsd2", "site_id"=>"57", "type_id"=>"3", "equipment_properties_attributes"=>{"0"=>{"id"=>"15", "value"=>"2015-10-34", "property_id"=>"4"}, "1"=>{"id"=>"16", "value"=>"fsdfdsfsd", "property_id"=>"5"}}})
(0.6ms) BEGIN
EquipmentProperty Load (0.7ms) SELECT "equipment_properties".* FROM "equipment_properties" WHERE "equipment_properties"."equipment_id" = $1 AND "equipment_properties"."id" IN (15, 16) [["equipment_id", 26]]
(0.2ms) COMMIT
=> true
irb(main):072:0> e.equipment_properties
EquipmentProperty Load (0.5ms) SELECT "equipment_properties".* FROM "equipment_properties" WHERE "equipment_properties"."equipment_id" = $1 [["equipment_id", 26]]
=> #<ActiveRecord::Associations::CollectionProxy [#<EquipmentProperty id: 15, equipment_id: 26, property_id: 4, value: "2015-10-34", created_at: "2015-11-02 15:26:51", updated_at: "2015-11-02 15:26:51">, #<EquipmentProperty id: 16, equipment_id: 26, property_id: 5, value: "fsdfdsfsd", created_at: "2015-11-02 15:26:51", updated_at: "2015-11-02 15:26:51">]>
irb(main):073:0> e = Equipment.find(26)
Equipment Load (0.5ms) SELECT "equipment".* FROM "equipment" WHERE "equipment"."id" = $1 LIMIT 1 [["id", 26]]
=> #<Equipment id: 26, name: "fdsfsdsdfsd2", created_at: "2015-11-02 15:26:43", updated_at: "2015-11-02 16:38:55", site_id: 57, type_id: 3>
irb(main):074:0> e.equipment_properties
EquipmentProperty Load (0.6ms) SELECT "equipment_properties".* FROM "equipment_properties" WHERE "equipment_properties"."equipment_id" = $1 [["equipment_id", 26]]
=> #<ActiveRecord::Associations::CollectionProxy [#<EquipmentProperty id: 15, equipment_id: 26, property_id: 4, value: "2015-10-30", created_at: "2015-11-02 15:26:51", updated_at: "2015-11-02 15:26:51">, #<EquipmentProperty id: 16, equipment_id: 26, property_id: 5, value: "fsdfdsfsd", created_at: "2015-11-02 15:26:51", updated_at: "2015-11-02 15:26:51">]>
The same thing is happening with the web interface. I can provide additional details if anyone needs them but I am allowing the parameters through and on creation, the initial values are saved.
I've been beating my head against this all morning and I suspect it is something stupid but I'm just not sure what to try next. Thanks!
UPDATE 1:
Equipment Model:
class Equipment < ActiveRecord::Base
belongs_to :site
belongs_to :type
has_and_belongs_to_many :properties
has_many :equipment_properties
accepts_nested_attributes_for :equipment_properties, reject_if: :all_blank, allow_destroy: true
end
And also the equipment_properties model:
class EquipmentProperty < ActiveRecord::Base
belongs_to :equipment
belongs_to :property
has_one :type, through: :equipment
end
Also, of relevance might be that I can update the individual equipment_property without nesting and that does work.
UPDATE 2:
I managed to add this to the controller and it saves the values now. Not pretty but it works I guess...
equipment_params[:equipment_properties_attributes].each do |property|
ep = EquipmentProperty.where(id: property[1][:id]).first
#logger.debug "EP Value: #{ep.value}"
#logger.debug "Property Value: #{property[1][:value]}"
ep.value = property[1][:value]
ep.save
end
This is what I ended up adding to the controller to resolve this. Definitely a hack though and I'm not sure why the updates are taking:
equipment_params[:equipment_properties_attributes].each do |property|
ep = EquipmentProperty.where(id: property[1][:id]).first
#logger.debug "EP Value: #{ep.value}"
#logger.debug "Property Value: #{property[1][:value]}"
ep.value = property[1][:value]
ep.save
end
I have 2 models with a one-to-many association: User and Recipe. the User class has_many :recipes while the Recipe class belongs_to :user. I've already run the migration, reloaded the rails console, and checked to make sure that user_id is a column in the recipes table. Still, I get an undefined method error when I try to append a recipe to a user:
2.0.0-p598 :047 > user.recipes << Recipe.first
NoMethodError: undefined method `recipes' for #<User:0x00000004326fa0>
here is the migration code (I've already run rake db:migrate):
class AddUserIdToRecipes < ActiveRecord::Migration
def change
add_column :recipes, :user_id, :integer
end
end
Here is the User model code:
class User < ActiveRecord::Base
has_one :profile
has_many :recipes
end
Here is the Recipe model code:
class Recipe < ActiveRecord::Base
validates_presence_of :title, :body
belongs_to :user
def long_title
"#{title} - #{published_at}"
end
end
Why does recipes still show up as an undefined method?
Try this on your console:
irb(main):007:0> user = User.new first_name: 'John', last_name: 'Doe'
=> #<User id: nil, first_name: "John", last_name: "Doe", created_at: nil, updated_at: nil>
irb(main):008:0> user.save
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "users" ("created_at", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?) [["created_at", "2015-01-19 21:14:33.489371"], ["first_name", "John"], ["last_name", "Doe"], ["updated_at", "2015-01-19 21:14:33.489371"]]
(0.6ms) commit transaction
=> true
irb(main):009:0> r = Recipe.new name: 'oooohh awesome', description: 'my description goes here'
=> #<Recipe id: nil, name: "oooohh awesome", description: "my description goes here", created_at: nil, updated_at: nil, user_id: nil>
irb(main):010:0> r.save
(0.1ms) begin transaction
SQL (0.2ms) INSERT INTO "recipes" ("created_at", "description", "name", "updated_at") VALUES (?, ?, ?, ?) [["created_at", "2015-01-19 21:15:16.548090"], ["description", "my description goes here"], ["name", "oooohh awesome"], ["updated_at", "2015-01-19 21:15:16.548090"]]
(1.2ms) commit transaction
=> true
irb(main):011:0> user.recipes << Recipe.first
Recipe Load (0.2ms) SELECT "recipes".* FROM "recipes" ORDER BY "recipes"."id" ASC LIMIT 1
(0.0ms) begin transaction
SQL (0.2ms) UPDATE "recipes" SET "updated_at" = ?, "user_id" = ? WHERE "recipes"."id" = 1 [["updated_at", "2015-01-19 21:15:49.181586"], ["user_id", 1]]
(1.3ms) commit transaction
Recipe Load (0.2ms) SELECT "recipes".* FROM "recipes" WHERE "recipes"."user_id" = ? [["user_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Recipe id: 1, name: "oooohh awesome", description: "sper long deskdk", created_at: "2015-01-19 21:10:24", updated_at: "2015-01-19 21:15:49", user_id: 1>]>
irb(main):012:0> user.save
(0.1ms) begin transaction
(0.0ms) commit transaction
=> true
irb(main):013:0> user.recipes
=> #<ActiveRecord::Associations::CollectionProxy [#<Recipe id: 1, name: "oooohh awesome", description: "sper long deskdk", created_at: "2015-01-19 21:10:24", updated_at: "2015-01-19 21:15:49", user_id: 1>]>
irb(main):014:0> user.recipes.first
=> #<Recipe id: 1, name: "oooohh awesome", description: "sper long deskdk", created_at: "2015-01-19 21:10:24", updated_at: "2015-01-19 21:15:49", user_id: 1>
irb(main):015:0>
you can see that Recipe.first has been inserted into user.recipes and its saved.
I made two models similar to yours, and have exactly the same setup as you. You can follow code above to write your controllers.
irb(main):001:0> hotel=Hotel.find(1)
←[1m←[36mHotel Load (1.0ms)←[0m ←[1mSELECT `hotels`.* FROM `hotels` WHERE `hotels`.`hotel_Id` = 1 LIMIT 1←[0m
=> #<Hotel hotel_Id: 1, hotel_Name: "Hotel Swosti", hotel_address: nil, hotel_location: "Bhubaneswar", hotel_contactNo: nil, crea
ted_at: nil, updated_at: nil>
irb(main):002:0> hotel.menus
←[1m←[35mMenu Load (1.0ms)←[0m SELECT `menus`.* FROM `menus` WHERE `menus`.`hotel_id` = 1
=> #<ActiveRecord::Associations::CollectionProxy []>
irb(main):003:0> first_menu=Menu.new(:menu_item_name=>'Rajma',:price=>30,:item_type=>'Half')
=> #<Menu hotel_Id: nil, menu_item_id: nil, menu_item_name: "Rajma", price: 30, item_type: "Half", created_at: nil, updated_at: n
il>
irb(main):004:0> first_menu.hotel
=> nil
irb(main):005:0> hotel.menus=first_menu
NoMethodError: undefined method `each' for #<Menu:0x512be78>
migration:
create_table :menus,:id=>false do |t|
t.integer 'hotel_Id'
t.primary_key 'menu_item_id'
t.string 'menu_item_name'
t.integer 'price'
t.string 'item_type'
end
add_index("menus","hotel_Id")
end
end
If you want to add first_menu to hotel.menus association, you should do:
hotel.menus << first_menu
The error occurs because Hotel#menus= setter expects collection of Menu objects as parameter.
hotel.menus is a relation. The association that you used returns an array of hotel menus.
To get the first member, a single menu, you could use hotel.menus.first.
If you want to create a new menu for a hotel, you'll probably be better off using:
hotel.menus.build(menu_item_name: 'Rajma', price: 30, item_type: 'Half')
hotel.save!
or the create form - depending on what else you want to do with the hotel or the menu, before you save.