Polymorphic association fails - ruby-on-rails

In my rails 3.2 application I have a User model and a Physician model with the following polymorphic associations:
User
class User < ActiveRecord::Base
attr_accessible :authenticatable_id, :authenticatable_type, :email
belongs_to :authenticatable, polymorphic: true
end
Physician
class Physician < ActiveRecord::Base
attr_accessible :name
has_one :user, as: :authenticatable
end
I wanted to test these out in the console and encountered a strange thing. Doing:
p = Physician.new
p.user.build
gives me NoMethodError: undefined method 'build' for nil:NilClass - but why would the physician's user attribute be nil?
Strangely, when I change the physician model to has_many :users instead of has_one :user and do
p = Physician.new
p.users.build
everything works fine.
What am I missing to get the has_one association to work?

You probably should do p.build_user since has_one doesn't add association.build method. You can also check apidock about methods has_one and has_many 'injects' into your model.

It is not entirely clear to me, but it seems that your are creating a Physician that is also a User. So it is able to make use of the features a User provides.
Your implementation creates two objects one Physician and oneUser, but when strictly looking at the situation, both are the same Physician/User.
So you should let Physician inherit from User:
class Physician < User
and remove the polymorphic relation between Physician and User.

Related

has_many :through association cannot find a valid model

I am creating association pretty much identical with the Rails Guides Patient-Appointment-Physician data model. A user has many prospects through prospect_subscription. However, when trying to access user.prospects in rails console, it throws the following error:
Rails couldn't find a valid model for Prospects association. Please provide the :class_name option on the association declaration. If :class_name is already provided, make sure it's an ActiveRecord::Base subclass. (NameError)
uninitialized constant User::Prospects (NameError)
Which is strange because all three models are right there. Migration has been run and sample data has been populated and can be checked in pgAdmin. Why can't Rails find the model?
Association defined at the models are as follows:
models/prospect.rb
class Prospect < ApplicationRecord
has_many :prospect_subscriptions
has_many :users, through: :prospect_subscriptions
end
models/user.rb
class User < ApplicationRecord
has_many :prospect_subscriptions
has_many :prospects, through: :prospect_subscriptions
end
models/prospect_subscription.rb
class ProspectSubscription < ApplicationRecord
belongs_to :user
belongs_to :prospect
end
I figured that wiping the database records clean and re-seeding helps. The difference is this time I assigned as user.prospects << [prospect_name], to make sure that the joins are created in the backend.

error with polymorphism and jsonapi-resources

I'm trying to setup a polymorphic association using the jsonapi-resources gem in Rails 5.
I have a User model that has a polymorphic association called profile, which can be of type Inspector or Buyer. Here are the truncated models:
class User < ApplicationRecord
belongs_to :profile, polymorphic: true
end
class Inspector < ApplicationRecord
belongs_to :user
end
class Buyer < ApplicationRecord
belongs_to :user
end
In the users table, there are corresponding profile_id and profile_type fields to represent the polymorphic association to inspectors and buyers. This all works as expected in our current Rails setup but I'm running into errors when trying to set this up for JSON:API using jsonapi-resources.
And now the corresponding jsonapi-resources resources and controllers (according to the directions):
class Api::V1::Mobile::UserResource < JSONAPI::Resource
immutable
attributes :name, :email
has_one :profile, polymorphic: true
end
class Api::V1::Mobile::ProfileResource < JSONAPI::Resource
end
class Api::V1::Mobile::ProfilesController < Api::V1::Mobile::BaseController
end
As far as I can tell, everything should now be setup properly but I get the following error when hitting the endpoint:
"exception": "undefined method `collect' for nil:NilClass",
"backtrace": [
".rvm/gems/ruby-2.6.5/gems/jsonapi-resources-0.10.2/lib/jsonapi/relationship.rb:77:in `resource_types'",
When digging into relationship.rb mentioned in the stack trace it looks like it can't get resolve the polymorphic types, so I tried the following:
class Api::V1::Mobile::UserResource < JSONAPI::Resource
immutable
attributes :name, :email
has_one :profile, polymorphic: true, polymorphic_types: ['inspector', 'buyer']
end
But alas, another error: Can't join 'User' to association named 'inspector'; perhaps you misspelled it?
Thanks in advance for any help with getting this setup!
The core problem actually has nothing to do with jsonapi-resources and is the associations. The inverse side of a belongs_to resource is always a has_one or has_many which points to the foreign key on the other table (and vice-versa).
class User < ApplicationRecord
belongs_to :profile, polymorphic: true
end
class Inspector < ApplicationRecord
has_one :user, as: :profile
end
class Buyer < ApplicationRecord
has_one :user, as: :profile
end
Having two belongs_to associations that point to each other would mean that you would have foreign keys on both sides - which is bad DB design due to the duplication (there should be only one source of truth) and won't really work in ActiveRecord since it will only ever write the foreign key on one side when you associate two models.

What is 'as' in ruby on rails model?

What is 'as' in ruby on rails model? and how does it work?
e.g.
has_many :something, as: :reasonable
Is it polymorphic?
Yes, this is a polymorphic association which allows a model to belong to multiple models. There should be
class Something < ApplicationRecord
belongs_to :reasonable, polymorphic: true
end
And then any model can have many of these as reasonable without adding another column to Something.
class Thing < ApplicationRecord
has_many :somethings, as: :reasonable
end
class Stuff < ApplicationRecord
has_many :somethings, as: :reasonable
end
Something stores both the class and ID of what its associated with allowing it to be polymorphic.

Rails :has_many of a :has_one through

I am having issues setting up model relations in Rails.
I have a User. A user can have many requests. A request can have one response. I set up my models like this:
Class User < ActiveRecord::Base
has_many :user_requests
has_many :request_responses, through: :user_requests
end
Class UserRequest < ActiveRecord::Base
belongs_to :user
has_one :request_response
end
Class RequestResponse < ActiveRecord::Base
belongs_to :user_request
end
Whenever I try to do something like:
UserRequest.request_response.id
I get errors that say either the relationship doesn't exist or the column does not exist in the table. Have I set up my relationships incorrectly?
You will get error:
UserRequest.request_response.id
Because:
request_response is expected to be a class method of UserRequest.
Association is defined as request_responses, not request_response, so calling user. request_response won't work either.
What to do?
call user.request_response_ids where user = User.first.

Why can't I call create on this has_one relationship?

I'm missing something basic. This is Rails 3.2.16.
In my app:
User has_one Subscription
Subscription belongs_to User
Subscription belongs_to Plan
But when I try to do this:
user.subscription.create(plan_id: plan.id)
I get the following:
NoMethodError: private method `create' called for #<Subscription:0x007f80f1f4dfc0>
Yet this works fine:
Subscription.create(user_id: user.id, plan_id: plan.id)
Perhaps you're looking for this method:
user.create_subscription(plan_id: plan.id)
From the RoR guide:
When initializing a new has_one or belongs_to association you must use the build_ prefix to build the association, rather than the association.build method that would be used for has_many or has_and_belongs_to_many associations. To create one, use the create_ prefix.
In your models you should have the associations like this:
class Subscription < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :subscription
end
Read here
one way to do that
user.subscription = Subscription.create(plan_id: plan.id)

Resources