Rails relationship undefined column - ruby-on-rails

I have Users and Absences
class User
has_many :absences
end
class Absence
belongs_to :student, foreign_key: "student_id", class_name: "User"
end
and my Absence migration has
t.integer :student_id, index: true, null: false
For some reason, I can say
Absence.first.student
but when I say
Absence.first.student.absences
I get
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column
absences.user_id does not exist) LINE 1: SELECT "absences".* FROM
"absences" WHERE "absences"."user_...
^ : SELECT "absences".* FROM "absences" WHERE "absences"."user_id" = $1 LIMIT $2
Obviously, I haven't set up sth right as it is looking for user_id instead of student_id but I have no idea why this happens... Thank you for any suggestions!

The reason you are getting this error because you didn't specify custom foreign key in has_many relation. Following code will solve your problem.
class User
has_many :absences, foreign_key: "student_id"
end
class Absence
belongs_to :student, foreign_key: "student_id", class_name: "User"
end

Related

How can I directly update the join model in a has_many_through association?

I have a Rails app with two models, joined by a third:
class Food < ApplicationRecord
has_many :shopping_list_items
has_many :users, through: :shopping_list_items
end
class ShoppingListItem < ApplicationRecord
belongs_to :user
belongs_to :food
end
class User < ApplicationRecord
has_many :shopping_list_items
has_many :foods, through: :shopping_list_items
end
The middle model, ShoppingListItem, has a few extra attributes, including priority which I'd like to update directly.
So for instance, I'd like to do something like:
r = current_user.shopping_list_items.where(food_id: 1).first
r.priority = "urgent"
r.save
The object looks fine until I try to save it, when I get a SQL error:
ActiveRecord::StatementInvalid (PG::SyntaxError: ERROR: zero-length delimited identifier at or near """"
LINE 1: ...$1, "updated_at" = $2 WHERE "shopping_list_items"."" IS NULL
^
: UPDATE "shopping_list_items" SET "priority" = $1, "updated_at" = $2 WHERE "shopping_list_items"."" IS NULL):
I guess it's complaining about the absence of a primary key? Not sure how to fix this, since the rails docs say that join tables shouldn't have a primary key column...
I created the middle table with a migration like this, :
create_table :shopping_list_items, id: false do |t|
t.belongs_to :user
t.belongs_to :food
t.string :priority
t.integer :position
t.timestamps
end

ActiveRecord::StatementInvalid on two namespaced models

I have to two models in the same namespace which have a habtm relation.
class Resource::Item < ApplicationRecord
has_and_belongs_to_many :resource_sets, foreign_key: 'resource_item_id', class_name: 'Resource::Set', table_name: 'resource_items_sets'
end
class Resource::Set < ApplicationRecord
has_and_belongs_to_many :resource_items, foreign_key: 'resource_set_id', class_name: 'Resource::Item', table_name: 'resource_items_sets'
end
The migration has been generated with rails g migration CreateJoinTableResourceItemsResourceSets resource_item resource_set
class CreateJoinTableResourceItemsResourceSets < ActiveRecord::Migration[5.2]
def change
create_join_table :resource_items, :resource_sets do |t|
# t.index [:resource_item_id, :resource_set_id]
# t.index [:resource_set_id, :resource_item_id]
end
end
end
So far everything looks great. The table resource_items_sets is being created with the two columns resource_item_id and resource_set_id.
This is the schema
create_table "resource_items_sets", id: false, force: :cascade do |t|
t.bigint "resource_item_id", null: false
t.bigint "resource_set_id", null: false
end
After creating an resource_item I get the following which is as expected.
pry(main)> Resource::Item.first.resource_sets
=> #<Resource::Set::ActiveRecord_Associations_CollectionProxy:0x3fdd08748004>
But doing the following throws an error. I was expecting 0.
pry(main)> Resource::Item.first.resource_sets.count
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column resource_items_sets.set_id does not exist
LINE 1: ...N "resource_items_sets" ON "resource_sets"."id" = "resource_...
^
: SELECT "resource_sets"."id" FROM "resource_sets" INNER JOIN "resource_items_sets" ON "resource_sets"."id" = "resource_items_sets"."set_id" WHERE "resource_items_sets"."resource_item_id" = $1 ORDER BY "resource_sets"."name" ASC
from /Users/username/.rvm/gems/ruby-2.6.0/gems/activerecord-5.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb:677:in `async_prepare'
Caused by PG::UndefinedColumn: ERROR: column resource_items_sets.set_id does not exist
LINE 1: ...N "resource_items_sets" ON "resource_sets"."id" = "resource_...
Where does set_id come from when resource_set_id has been declare everywhere? How can I fix this issue? I want to keep the namespaces for both since I might end up creating items and set for more namespaces.
Thanks so much, guys!
You need to set the foreign keys on both sides of the join table because they can't be inferred in your case.
In this case the correct has_and_belongs_to_many calls should look like:
has_and_belongs_to_many :resource_items,
foreign_key: 'resource_set_id',
association_foreign_key: 'resource_item_id',
class_name: 'Resource::Item',
join_table: 'resource_items_sets'
end
and
class Resource::Item < ApplicationRecord
has_and_belongs_to_many :resource_sets,
foreign_key: 'resource_item_id',
association_foreign_key: 'resource_set_id',
class_name: 'Resource::Set',
join_table: 'resource_items_sets'
end
Note the added association_foreign_key option specifying the other foreign key in the join table.

undefined column in rails many to many

So i defined a has_and_belongs_to_many relationship between my 2 models as shown below
class Client < ActiveRecord::Base
attr_accessible :first_name, :last_name, :email
has_and_belongs_to_many :themes, class_name: 'XY::Theme'
end
class XY::Theme < ActiveRecord::Base
has_and_belongs_to_many :clients
end
then i defined my join table from the active record rails guide like this
class CreateClientsThemesJoinTable < ActiveRecord::Migration
def change
create_table :clients_xy_themes, id: false do |t|
t.integer :client_id
t.integer :xy_theme_id
end
add_index :clients_xy_themes, :client_id
add_index :clients_xy_themes, :xy_theme_id
end
end
but when i try to access the themes from clients table in rails console, iget this error
PG::UndefinedColumn: ERROR: column clients_xy_themes.theme_id does not exist
LINE 1: ...ER JOIN "clients_xy_themes" ON "xy_themes"."id" = "clients_v...
why is this happening? My migration specifically stated the keys on the themes table , but its trying to access a column that doesnt exist
The convention is, if your relation name is themes, then the foreign key name would be theme_id.
You could define the relation like:
has_and_belongs_to_many :xy_themes, class_name: 'XY::Theme'
Or you have to define the association_foreign_key option.
has_and_belongs_to_many :themes, class_name: 'XY::Theme', association_foreign_key: 'xy_theme_id'

belongs_to with foreign_key not working in one direction

class Position < ActiveRecord::Base
belongs_to :product, foreign_key: :symbol
end
class Product < ActiveRecord::Base
has_many :positions, primary_key: :symbol, foreign_key: :symbol
end
When I do
Product.first.positions.first I am getting a Product back.
But, when I do Position.first.product I am getting nothing.
When I look at the SQL generated by the query, it is:
SELECT "products.*" FROM "products" WHERE "products.id" = ? LIMIT 1 [["id", 0]]
Why?
The SQL generated is using products.id instead of products.symbol because you didn't tell it that the association should use symbol as the primary key instead of the default of id. So, in your Position class just add primary_key: :symbol to the belongs_to and I think that'll do it.
Try this:
class Product < ActiveRecord::Base
self.primary_key = "symbol"
end
First of all, you need to revise the creation of your model Product.
You need to create it the following way:
class CreateProducts < ActiveRecord::Migration
def change
create_table :products, id: false do |t|
t.string :symbol, null: false
t.timestamps
end
add_index :products, :symbol, unique: true
end
end
And then let your model know about the primary_key, that is not id:
class Product < ActiveRecord::Base
self.primary_key = "symbol"
end
And after that, when you do Product.last, it will generate the following query:
Product.last
# Product Load (0.3ms) SELECT "products".* FROM "products" ORDER BY "products"."symbol" DESC LIMIT 1

Strange query when calling has_one

Problem: Message class with a has_many :through relationship and a has_one relationship both to the User class. Getting a strange query when I try to use the has_one relationship.
I have a Message class with a has_many relationship to a User through message_memberships. Each instance of Message as well as having users linked to it through the has_many through relationship has a creator who is also a User.
Due to laziness I started to log the creators id in a creator_id column on the Message instance (schema below). Every time a message was created I would add the creators id to the column. Every time I wanted to reference the creator I would call User.find(message.creator_id). I was looking in to creating a link between the creator column and a user but I cannot find the right implementation.
The problem I am having is with referencing the :creator_id column in the has_one relationship. I would assume something like this would work
Message.rb
has_one :creator, -> { where id: :creator_id }, class_name: 'User'
But the query that gets called whenever I look for message.creator is this
SELECT "users".* FROM "users" WHERE "users"."message_id" = ? AND "users"."id" = 'creator_id' LIMIT 1 [[nil, 26]]
I am not entirely sure where the WHERE "users"."message_id" = ?. Without it, it looks like the query would be fine. I have no idea how to stop it from happening.
Schema
create_table "messages", force: true do |t|
t.string "title"
t.string "contents"
t.datetime "created_at"
t.datetime "updated_at"
t.string "token"
t.integer "creator_id"
t.string "link"
end
Any help would be greatly appreciated
Try this setup:
class Message
belongs_to :creator, class_name: '::User', foreign_key: :creator_id
end
class User
has_many :authored_messages, class_name: '::Message', inverse_of: :creator
end

Resources