Adding one-to-many association on namespaced models - ruby-on-rails

I'm trying to set up a one-to-many relationship between two namespaced models, and despite my best efforts can't catch where my problem is.
Here is my migration:
class AddAssociations < ActiveRecord::Migration[5.1]
def change
add_belongs_to :admin_plans, :academy_courses, foreign_key: true, index: true
add_reference :academy_courses, :admin_plans, foreign_key: true, index: true
end
end
Admin::Plan model:
class Admin::Plan < ApplicationRecord
belongs_to :course, class_name: 'Academy::Course', optional: true, foreign_key: :academy_courses_id
end
Academy::Course model:
class Academy::Course < ApplicationRecord
has_many :plans, class_name: 'Admin::Plan, foreign_key: :admin_plans_id
end
Here's what happens when I try to get all plans for a course:
irb(main):001:0> c = Academy::Course.new
=> #<Academy::Course id: nil, title: nil, description: nil, user_id: nil, created_at: nil, updated_at: nil, admin_user_plans_id: nil, admin_plans_id: nil>
irb(main):004:0> c.plans
=> #<ActiveRecord::Associations::CollectionProxy []>
irb(main):005:0> c.plans.all
Traceback (most recent call last):
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column admin_plans.course_id does not exist)
LINE 1: SELECT "admin_plans".* FROM "admin_plans" WHERE "admin_plan...
^
: SELECT "admin_plans".* FROM "admin_plans" WHERE "admin_plans"."course_id" = $1 LIMIT $2
irb(main):007:0> Admin::Plan.last
Admin::Plan Load (1.0ms) SELECT "admin_plans".* FROM "admin_plans" ORDER BY "admin_plans"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<Admin::Plan id: 2, name: "MOMLab", price: 15, created_at: "2019-02-06 15:05:43", updated_at: "2019-02-07 07:12:26", academy_courses_id: nil>
I'd really appreciate ideas of where this admin_plans.course_id is coming from, since I declared that the foreign key is :academy_courses_id.
Thanks :)

After a lot of banging my head on the wall and trying every method on Google, I settled for this:
Migration:
class AddCourseIdToAcademyLessons < ActiveRecord::Migration[5.1]
def change
add_column :admin_plans, :course_id, :integer, foreign_key: true, index: true
end
end
Admin::Plan class:
class Admin::Plan < ApplicationRecord
belongs_to :course, class_name: 'Academy::Course', optional: true
end
Academy::Course class:
class Academy::Course < ApplicationRecord
has_many :plans, class_name: 'Admin::Plan'
end

Related

NoMethodError: undefined method `categories' for #<Product:0x00000000070a9810>

I am trying to add categories for some products on Ruby on Rails, but I am getting an error saying NoMethodError: undefined method `categories' for #
Here is my product.rb :
class Product < ApplicationRecord
validates :title, presence: true
has_many :product_categories
has_many :categories, through :product_categories
end
the product category app record:
class ProductCategory < ApplicationRecord
belongs_to :product
belongs_to :category
end
and the category.rb:
class Category < ApplicationRecord
has_many :product_categories
has_many :products, through :product_categories
end
On the interactive ruby shell I give the following commands:
irb(main):006:0> Product.second!
Product Load (0.5ms) SELECT "products".* FROM "products" ORDER BY
"products"."id" ASC LIMIT ? OFFSET ? [["LIMIT", 1], ["OFFSET", 1]]
=> #<Product id: 2, title: "bread", description: "with glutein", price:
0.6e0, created_at: "2019-01-13 19:42:45", updated_at: "2019-01-13
19:42:45">
irb(main):007:0> product= _
=> #<Product id: 2, title: "bread", description: "with glutein", price:
0.6e0, created_at: "2019-01-13 19:42:45", updated_at: "2019-01-13 19:42:45">
irb(main):008:0> product = _
=> #<Product id: 2, title: "bread", description: "with glutein", price:
0.6e0, created_at: "2019-01-13 19:42:45", updated_at: "2019-01-13 19:42:45">
<duct.categories.create!(title: "Bread")
NoMethodError: undefined method `categories' for
Could someone help me understand why this error appears? I am following this tutorial https://www.youtube.com/watch?v=TwoafJC7vlw and it seems to work fine there.
In the Product model please change has_many :categories, through :product_categories to has_many :categories, through: :product_categories. You need to add a colon after through. Same in Category model. This should solve the error.
In addition you have some other syntax errors / typos, for example in product model validates is written as valitates.

Ruby on Rails - Polymorphic Association with Categories

I'm currently trying to implement a Category model to my application. I'm trying to design it in a way that Users can have many Categories, and so can Groups.
The problem I'm running into is that I also want to be able to just have a normal list of Categories without them being assigned to any User or Group.
I was referencing rubyonrails.org/association_basics.
class CreateCategories < ActiveRecord::Migration[5.0]
def change
create_table :categories do |t|
t.string :name
t.text :description
t.references :categorizable, polymorphic: true, index: true
t.timestamps
end
end
end
class Category < ApplicationRecord
belongs_to :categorizable, :polymorphic => true
end
class User < ApplicationRecord
has_many :categories, :as => :categorizable
end
class Group< ApplicationRecord
has_many :categories, :as => :categorizable
end
I'm trying to create a new Category through rails c, but whenever I try to save, it rolls back my transaction probably because I'm missing some condition.
Category(id: integer, name: string, description: text, created_at: datetime, updated_at: datetime)
Category.create( :id => 1, :name => 'Category_1', :description => '' )
begin transaction
rollback transaction
I also feel like there is a better way to create a new Category, as I shouldn't be setting the id manually.
Thanks for your help.
In rails 5, whenever you define a belongs_to association, it is required to have the associated record present by default. You would see this when you look at the errors after trying to create the category object
category = Category.create(:name => 'Category_1', :description => '' )
category.errors.full_messages.to_sentence
If you want to be able to save a record without the belongs_to association, you would have to specify it explicitly
class Category < ApplicationRecord
belongs_to :categorizable, polymorphic: true, required: false
end
If you try to create a new category and see the error is that there needs to exists a categorizable record in order for the category to be created, an easy way to do it is to put the new object itself as the categorizable one and it should do the trick.
$ category = Category.new
=> #<Category id: nil, name: nil, description: nil, categorizable_type: nil, categorizable_id: nil, created_at: nil, updated_at: nil>
$ category.save
(0.1ms) begin transaction
(0.1ms) rollback transaction
=> false
$ category.errors.full_messages
=> ["Categorizable must exist"]
$ category = Category.new(categorizable: category)
=> #<Category id: nil, name: nil, description: nil, categorizable_type: "Category", categorizable_id: nil, created_at: nil, updated_at: nil>
$ category.save
(0.1ms) begin transaction
SQL (1.3ms) INSERT INTO "categories" ("categorizable_type", "created_at", "updated_at") VALUES (?, ?, ?) [["categorizable_type", "Category"], ["created_at", 2017-01-15 00:08:55 UTC], ["updated_at", 2017-01-15 00:08:55 UTC]]
(0.7ms) commit transaction
=> true
This should help, Rails Cast on Polymorphic
https://www.youtube.com/watch?v=6l9EAuev16k
You can create polymorphic records with this...
`#category = #categorizable.User.new`
`#category = #categorizable.Group.new`
So you do not need the id.

Rails polymorphic relations

I'm trying to use polymorphic relationship with rails 5.
I have difficulties to figure out how to finish my relations.
I have users, who can take reservations for hotels, restaurants, etc.
My purpose is to get hotels name and reservations when calling /users/{id} through API.
Here is my User model :
class User < ApplicationRecord
has_many :reservations, as: :reservable
has_many :hotels, through: :reservations
end
My Hotel model :
class Hotel < ApplicationRecord
has_many :reservations, as: :reservable
belongs_to :users, through: :reservations
end
My Reservation model :
class Reservation < ApplicationRecord
belongs_to :reservable, polymorphic: true
belongs_to :users
belongs_to :hotels
end
Migrations :
User :
class CreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.timestamps
end
end
end
Reservations :
class CreateReservations < ActiveRecord::Migration[5.0]
def change
create_table :reservations do |t|
t.belongs_to :hotel, index: true
t.belongs_to :user, index: true
t.datetime :date_from
t.datetime :date_to
t.timestamps
end
end
end
class ReservationPolymorphism < ActiveRecord::Migration[5.0]
def change
rename_column :reservations, :hotel_id, :reservable_id
change_column :reservations, :reservable_id, polymorphic: true
add_column :reservations, :reservable_type, :string
end
end
Hotel :
class CreateHotels < ActiveRecord::Migration[5.0]
def change
create_table :hotels do |t|
t.string :name
t.string :address
t.string :postal_code
t.string :town
t.timestamps
end
end
end
I just have 1 line in my reservations table :
mysql> select * from reservations;
+----+---------------+-----------------+---------+---------------------+---------------------+---------------------+---------------------+
| id | reservable_id | reservable_type | user_id | date_from | date_to | created_at | updated_at |
+----+---------------+-----------------+---------+---------------------+---------------------+---------------------+---------------------+
| 1 | 1 | Hotel | 1 | 2017-01-12 00:00:00 | 2017-01-15 00:00:00 | 2016-10-19 09:18:01 | 2016-10-19 09:18:01 |
+----+---------------+-----------------+---------+---------------------+---------------------+---------------------+---------------------+
I have no result when using the API.
Here is what I get, using Rails console :
2.2.3 :001 > thomas = User.find(1)
User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User id: 1, first_name: "Thomas", last_name: "Dupont", email: "thomas.dupont#yopmail.com", created_at: "2016-10-18 21:12:12", updated_at: "2016-10-18 21:12:12">
2.2.3 :003 > thomas.reservations
Reservation Load (0.3ms) SELECT `reservations`.* FROM `reservations` WHERE `reservations`.`reservable_id` = 1 AND `reservations`.`reservable_type` = 'User'
2.2.3 :005 > thomas.hotels
NameError: uninitialized constant User::Hotels
So I can see I make basic mistake with rails relations and polymorphism but I really can't find out where I'm wrong.
I think I made a mistake.
I assumed Polymorphism could be use to load other models and their tables (like "morphTo" with eloquent / laravel), whereas it's just to load a model which has no data representation (as described in this post : https://robots.thoughtbot.com/using-polymorphism-to-make-a-better-activity-feed-in-rails )
I guess on viewing your code, you have written extra associations on top of the polymorphic association,
To create a polymorphic association, just the following code is enough. No need to pass extra association inside the model. Please refactor your code as follows,
User model
class User < ApplicationRecord
has_many :reservations, as: :reservable
end
Hotel model :
class Hotel < ApplicationRecord
has_many :reservations, as: :reservable
end
Reservation model :
class Reservation < ApplicationRecord
belongs_to :reservable, polymorphic: true
end
And in the migration file for reservations, add the reservable as below
t.references :reservable, polymorphic: true
Ok i sorted this out :
This is the reservation model :
class Reservation < ApplicationRecord
belongs_to :reservable, polymorphic: true
end
This is the hotel model :
class Hotel < ApplicationRecord
end
And the user model :
class User < ApplicationRecord
has_many :reservations
end
And how I got a results in rails console :
Running via Spring preloader in process 87452
Loading development environment (Rails 5.0.0.1)
2.2.3 :001 > u = User.find(1)
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User id: 1, first_name: "Thomas", last_name: "Dupont", email: "thomas.dupont#yopmail.com", created_at: "2016-10-18 21:12:12", updated_at: "2016-10-18 21:12:12">
2.2.3 :002 > u.reservations
Reservation Load (0.3ms) SELECT `reservations`.* FROM `reservations` WHERE `reservations`.`user_id` = 1
=> #<ActiveRecord::Associations::CollectionProxy [#<Reservation id: 1, reservable_id: 1, reservable_type: "Hotel", user_id: 1, date_from: "2017-01-12 00:00:00", date_to: "2017-01-15 00:00:00", created_at: "2016-10-19 09:18:01", updated_at: "2016-10-19 09:18:01">]>
2.2.3 :003 > u.reservations.first.reservable
Hotel Load (0.3ms) SELECT `hotels`.* FROM `hotels` WHERE `hotels`.`id` = 1 LIMIT 1
=> #<Hotel id: 1, name: "HYATT REGENCY HOTEL", address: "3 Place du Général Kœnig", postal_code: "75017", town: "PARIS", created_at: "2016-10-19 08:36:55", updated_at: "2016-10-19 08:36:55">

Rails string as a foreign key

I have a relation between User and Course (typical enrollment data). A User has_many Course and vice-versa (typical JOIN table scenario).
I am attempting to migrate my previous has_and_belongs_to_many relationship between these two models to a has_many :through relationship. My files currently look like:
class User < ActiveRecord::Base
has_and_belongs_to_many :courses
end
and
class Course < ActiveRecord::Base
has_and_belongs_to_many :users
end
and the table name that joins the two models is courses_users.
I now need to migrate this relationship to the has_many :through association, and also make the column type of user_id a string, as I want to use the g_number (string) attribute of User as the foreign key. Note: I don't care about the performance difference between int and varchar/string.
The short and simple problem is that I need users.g_number to reference enrollments.user_id as a foreign key, and both are strings.
My attempt at a migration and model rework is this:
class User < ActiveRecord::Base
has_many :enrollment
has_many :courses, :through => :enrollment
end
and
class Course < ActiveRecord::Base
has_many :enrollment
has_many :users, :through => :enrollment
end
lastly
class Enrollment < ActiveRecord::Base
belongs_to :course
belongs_to :user
end
then the migration
class ChangeUserIdJoin < ActiveRecord::Migration
def self.up
rename_table :courses_users, :enrollments
end
def self.down
rename_table :enrollments, :courses_users
end
end
Everything works fine here. I can do queries like User.courses and Course.users. But now I want to change the type of the user_id column in the join table to a string so that I can store the g_number (string attribute on User) and join on that instead of the serial id column of User.
When I attempt to change the user_id column type to string in the migration:
class ChangeUserIdJoin < ActiveRecord::Migration
def self.up
change_column :courses_users, :user_id, :string
rename_table :courses_users, :enrollments
end
def self.down
rename_table :enrollments, :courses_users
change_column :courses_users, :user_id, :integer
end
end
the queries Course.users and User.courses start failing (below from Rails console). User.courses returns an empty array (whereas before there are multiple Course objects), and Course.users throws an exception because of mismatched column types (which obviously makes sense):
u = User.take
User Load (0.9ms) SELECT "users".* FROM "users" LIMIT 1
=> #<User id: 1, username: "director", g_number: "g00000000", password_digest: "$2a$10$dvcOd3rHfbcR1Rn/D6VhsOokj4XiIkQbHxXLYjy5s4f...", created_at: "2016-01-06 01:36:00", updated_at: "2016-01-06 01:36:00", first_name: "Director", last_name: "", role: 0, registered: true>
2.1.5 :002 > u.courses
Course Load (0.9ms) SELECT "courses".* FROM "courses" INNER JOIN "enrollments ON "courses"."id" = "enrollments"."course_id" WHERE "enrollments"."user_id" = $1 [["user_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy []>
2.1.5 :003 > c = Course.take
Course Load (0.7ms) SELECT "courses".* FROM "courses" LIMIT 1
=> #<Course id: 12754, year: 2015, semester: 0, department: 7, course: 101, section: 1, name: "SPA 101 01 - Elementary Spanish I">
2.1.5 :004 > c.users
PG::UndefinedFunction: ERROR: operator does not exist: integer = character varying
LINE 1: ... "users" INNER JOIN "enrollments" ON "users"."id" = "enrollm...
I need to be able to join on enrollments.user_id = users.g_number. What do I need to do in order to change the user_id column to a string type in the Enrollment model/table, and still be able to do Active Record queries like User.courses and Course.users?
Try by specifying the foreign and primary keys in enrollments model, like this
belongs_to :user foreign_key: :user_id, primary_key: :g_number

has_many association using foreign_key option seem not to work?

I'm new to Rails and I'm having an issue with models using "different" primary/foreign key naming conventions than supported by Rails. (OK, I think this MIGHT be the problem)
So these are my 2 models:
class Project < ActiveRecord::Base
self.primary_key = "PROJECT_ID"
has_many :employees, :foreign_key => "PROJECT_ID"
end
class Employee < ActiveRecord::Base
self.primary_key = "EMPLOYEE_ID"
belongs_to :project, :primary_key => "PROJECT_ID"
end
And this is what's driving me nuts:
> p = Project.find(2)
Project Load (0.2ms) SELECT "projects".* FROM "projects" WHERE "projects"."PROJECT_ID" = ? LIMIT 1 [[nil, 2]]
=> #<Project project_id: 2, name: "Project 2", created_at: "2013-08-18 21:26:33.538007", updated_at: "2013-08-18 21:26:33.538007">
> p.employees.inspect
Employee Load (0.2ms) SELECT "employees".* FROM "employees" WHERE "employees"."PROJECT_ID" = ? **[[nil, nil]]**
=> "#<ActiveRecord::Associations::CollectionProxy []>"
For some reason I don't receive the employees with project_id = 2. It seems that the ? gets substituted with nil.
It works the other way round, check this out
> e = Employee.find_by_project_id(2)
Employee Load (0.2ms) SELECT "employees".* FROM "employees" WHERE "employees"."project_id" = 2 LIMIT 1
=> #<Employee employee_id: 2, first_name: "Will", last_name: "Smith", project_id: 2, created_at: "2013-08-18 21:21:47.884919", updated_at: "2013-08-18 21:22:48.263970">
> e.project.inspect
Project Load (0.2ms) SELECT "projects".* FROM "projects" WHERE "projects"."PROJECT_ID" = ? ORDER BY "projects"."PROJECT_ID" ASC LIMIT 1 [[nil, 2]]
=> "#<Project project_id: 2, name: \"Project 2\", created_at: \"2013-08-18 21:26:33.538007\", updated_at: \"2013-08-18 21:26:33.538007\">"
What am I missing?
Try this:
class Project < ActiveRecord::Base
self.primary_key = "PROJECT_ID"
has_many :employees
end
class Employee < ActiveRecord::Base
attr_accessible :project
self.primary_key = "EMPLOYEE_ID"
belongs_to :project
end
Try to avoid upper case column names at all cost.
for the record here my schema.rb
create_table "employees", :primary_key => "EMPLOYEE_ID", :force => true do |t|
t.integer "project_id"
end
create_table "projects", :primary_key => "PROJECT_ID", :force => true do |t|
end
Try the following:
class Project < ActiveRecord::Base
self.primary_key = "PROJECT_ID"
has_many :employees, :foreign_key => "PROJECT_ID", :primary_key => "PROJECT_ID"
end
class Employee < ActiveRecord::Base
self.primary_key = "EMPLOYEE_ID"
belongs_to :project, :primary_key => "PROJECT_ID", :foreign_key => "PROJECT_ID"
end
If you use uppercased field names (i am not sure based on your question) then make sure you always use uppercased names (e.g. find_by_PROJECT_ID). Rails and ActiveRecord are case sensitive.

Resources