Rails belongs_to/has_many with external Model - ruby-on-rails

In my Rails 5.2 application I want to reference another Model. I have the following setup in the application:
class SomeModule::AnotherModule::User < ApplicationRecord
has_many :phones
end
class Phone < ApplicationRecord
belongs_to :user, optional: true, class_name: '::SomeModule::AnotherModule::User'
end
The migration was done like so:
add_reference :phones, :user, foreign_key: true, index: true
Now, when I try to call upon the user from a phone I get this:
Phone.first.user
#=> NameError: uninitialized constant User::Phone
from /home/testuser/.rvm/gems/ruby-2.5.1/gems/activerecord-5.2.0/lib/active_record/inheritance.rb:196:in `compute_type'
Removing the class_name: attribute does not change anything.
What am I doing wrong?

class SomeModule::AnotherModule::User < ApplicationRecord
has_many :phones, class_name: 'Phone', foreign_key: 'user_id'
end
class Phone < ApplicationRecord
belongs_to :user, optional: true, class_name: 'SomeModule::AnotherModule::User', foreign_key: 'user_id'
end

Related

Acitverecord specify `inverse_of` with `through` but not the other way

I have 3 models as follows :
class User
has_many :event_series, inverse_of: :user
has_many :events, through: :event_series, inverse_of: :user
end
class EventSeries
belongs_to :user, inverse_of: :event_series
has_many :events, inverse_of: :event_series
end
class Event
belongs_to :event_series, inverse_of: :events
has_one :user, through: :event_series, inverse_of: :events
end
This is all fine.
Now I want to add a special event for each user called the 'showcase_event'.
class User
has_one :showcase_event, class_name: 'Event', inverse_of: :user
end
This isn't working because the Event model doesn't have the user directly, it's associated through EventSeries.
I'm getting an error during serialization:
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column events.user_id does not exist
I'm using fast jsonapi:
class PublicUserSerializer
include FastJsonapi::ObjectSerializer
...
has_one :showcase_event, record_type: :event, serializer: EventSerializer
...
end
It seems to me that the inverse relationship that I have between User, Event and EventSeries needs to work between User and showcase_event but I don't know how to specify that ONLY the inverse is through EventSeries
Apologies in advance for not having the best vocabulary to describe this problem.
Solved by my rubber duck.
As seen in the documentation :
The #belongs_to association is always used in the model that has the
foreign key.
So I changed my declaration of the showcase_event to this :
class User
belongs_to :showcase_event, class_name: 'Event', inverse_of: :user, optional: true
end
And problem solved

Has one and has many of itself

I have a user instance that has many invitees but only one inviter.
I am trying to access the inviter instance associated with that user and also his invitees.
i.e:
user.inviter #=> return another user instance.
user.invitees #=> return a collection on user instances
User.rb
class User < ActiveRecord::Base
has_one :inviter, class_name: Invitation, foreign_key: :invitee_id
has_many :invitees, class_name: Invitation, foreign_key: :inviter_id
end
Invitation.rb
class Invitation < ActiveRecord::Base
belongs_to :inviter, class_name: User, foreign_key: :inviter_id
belongs_to :invitee, class_name: User, foreign_key: :invitee_id
end
migration
class CreateInvitations < ActiveRecord::Migration
def change
create_table :invitations do |t|
t.references :inviter, references: :user, index: true
t.references :invitee, references: :user, index: true
t.foreign_key :users, column: :inviter_id
t.foreign_key :users, column: :invitee_id
t.timestamps
end
end
end
This works half of the way because if I call user.inviter on a user that has an inviter it will return the invitation instance but not the user like I would like. Same for user.invitees returns a collection on invitation instances.
Do y'all have an idea of how to make it work ?
Your should use through option like this:
class User < ActiveRecord::Base
has_one :invitation, inverse_of: :inviter
has_one :inviter, through: :invitation
has_many :invitations, inverse_of: :invitee
has_many :invitees, through: :invitations
end
class Invitation < ActiveRecord::Base
belongs_to :inviter, class_name: User, inverse_of: :invitation
belongs_to :invitee, class_name: User, inverse_of: :invitations
end
user.invitees will give collection of invitation records.
Using IN query into User model with all invitee_id which reference to user model will give you collection of users.
user_ids = user.invitees.map(&:invitee_id)
User.where(id: user_ids)

Polymorphics in Rails ' uninitialized constant' error

I wanted to make polymorphic association in my project so I followe rails guide to do it but I got: NameError: uninitialized constant DashboardAssignment::Assignable
error.
Models:
DashboardAssignment:
class DashboardAssignment < ActiveRecord::Base
belongs_to :dashboard
belongs_to :assignable, polymorphic: true
validates :dashboard, presence: true
validates :assignable, presence: true, uniqueness: { :scope => :dashboard }
end
User:
class User < ActiveRecord::Base
has_many :dashboard_assignments, as: :assignable
has_many :dashboards, through: :dashboard_assignments
end
Group:
class Group < ActiveRecord::Base
has_one :dashboard_assignment, as: :assignable
has_many :laboratories_assignments, as: :lab_assignable
end
Dashboard:
class Dashboard < ActiveRecord::Base
has_one :building
has_many :dashboard_assignments
has_many :users, through: :dashboard_assignments, as: :assignable
end
In DashboardAssignment Migration file I have:
t.references :assignable, null: false, polymorphic: true, index: true
When I try to create new DashboardAssignment by DashboardAssignment.create(assignable: u) where u = User.find(1)
it gives me error:
NameError: uninitialized constant DashboardAssignment::Assignable
from P:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/activerecord-4.2.6/lib/active_record/inheritance.rb:158:in `compute_type' ...
Am I doing something wrong?
My rails version is 4.2.6
You should be using source instead of as in a through association
class Dashboard < ActiveRecord::Base
has_one :building
has_many :dashboard_assignments
has_many :users, through: :dashboard_assignments, source: :assignable
end
I would caution though that this might give you assignables that are of class Group. The best way to go around this issue is by adding a condition to a has_many for dashboard_assignments that let's you filter only for dashboard_assignments that have assignable_type = 'User':
class Dashboard < ActiveRecord::Base
has_one :building
has_many :user_dashboard_assignments, -> { where(assignable_type: 'User') }, class_name: 'DashboardAssignment'
has_many :users, through: :user_dashboard_assignments, source: :assignable
end

Multiple Associations With the Same Table rails

i have two class User and Bug there are two foreign keys in bug which are referencing to user_id ..the problem is that how i store user_id in foreign key column while creating the record.like for example if user enter bug then his id store in buger_id colunm.
class Bug
belongs_to :buger, class_name: "User", foreign_key: "buger_id"
belongs_to :developer , class_name: "User", foreign_key: "developer_id"
class user
has_many :created_bugs, class_name:"bugs"
has_many :developed_bugs, class_name:"bugs"
You need to add the foreign_key to the has_many declaration!
class User < ActiveRecord::Base
has_many :created_bugs, class_name: 'Bug' , foreign_key: :buger_id
has_many :developed_bugs, class_name: 'Bug' , foreign_key: :developer_id
end
class Bug < ActiveRecord::Base
belongs_to :buger, class_name: 'User'
belongs_to :developer , class_name: 'User'
end
See also: http://guides.rubyonrails.org/association_basics.html
You can specify class and foreign key on the has_many line as well.
has_many :created_bugs, class_name:"Bug", foreign_key: 'buger_id'
has_many :developed_bugs, class_name:"Bug", foreign_key: 'developer_id'
In Rails 5.1 or greater you can do it like this:
Migration
class CreateBug < ActiveRecord::Migration
def change
ccreate_table(:bugs) do |t|
t.references :bugger, foreign_key: { to_table: 'users' }
t.references :developer, foreign_key: { to_table: 'users' }
end
end
end
This will create the fields bugger_id, and developer_id and make the database level references to the users table
Models
class Bug < ActiveRecord::Base
belongs_to :bugger, class_name: "User"
belongs_to :developer, class_name: "User"
end
class User < ActiveRecord::Base
has_many :created_bugs, class_name: "Bug", foreign_key: "bugger_id"
has_many :developed_bugs, class_name: "Bug", foreign_key: "developer_id"
end
FactoryBot
If you use FactoryBot then your factory might look something like this:
FactoryBot.define do
factory :bug do
association :bugger, factory: :user
association :developer, factory: :user
end
end

has_many :through looking for inexistent column

I have these 3 classes:
User:
class User < ActiveRecord::Base
end
UserStory:
class UserStory < ActiveRecord::Base
belongs_to :owner, class_name: 'User'
belongs_to :assigned, class_name: 'User'
belongs_to :board
has_many :comments
has_many :watched_stories
has_many :watchers, through: :watched_stories, source: :user
end
WatchedStory:
class WatchedStory < ActiveRecord::Base
belongs_to :user
belongs_to :story, class_name: 'UserStory'
end
when I try to list all watchers via UserStory#watchers I see this error:
PG::UndefinedColumn: ERROR: column watched_stories.user_story_id does not exist
It seems like the relation has_many through is wrong, but I can see the error. What am I missing here?
My migration:
class CreateWatchedStories < ActiveRecord::Migration
def change
create_table :watched_stories do |t|
t.references :user, index: true
t.references :story, index: true, references: :user_story
t.timestamps
end
end
end
If WatchedStory and UserStory are connected through story_id you need to specify that, otherwise Rails will assume it's user_story_id:
class WatchedStory < ActiveRecord::Base
belongs_to :story, class_name: 'UserStory', foreign_key: :story_id
end
class UserStory < ActiveRecord::Base
has_many :watched_stories, foreign_key: :story_id
end
PG::UndefinedColumn: ERROR: column watched_stories.user_story_id does
not exist
Rails is looking for a column called user_story_id which didn't exist in your watched_stories table.
Reason
You have this line belongs_to :story, class_name: 'UserStory' in WatchedStory model,so with class_name specified as UserStory,Rails will look for user_story_id by default.
Fix
The error could be resolved by setting foreign_key option in your WatchedStory model
class WatchedStory < ActiveRecord::Base
belongs_to :user
belongs_to :story, class_name: 'UserStory',foreign_key: :story_id
end

Resources