Rails :has_many of a :has_one through - ruby-on-rails

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.

Related

Rails 4 API: Creating a nested resource

I have 3 models:
class Repositioning < ActiveRecord::Base
has_one :repo_mood
has_one :mood, through: :repo_mood
end
class Mood < ActiveRecord::Base
has_many :repo_moods
has_many :repositionings, through: :repo_moods
end
class RepoMood < ActiveRecord::Base
belongs_to :repositioning
belongs_to :mood
end
But I only have a Repositionings controller. In my app, the user can add a mood to the repositioning and the data is sent to my API as:
repositioning: { mood: mood_id }
Is there a railsy way of generating the necessary repo_mood entry:
RepoMood.create(repositioning_id: repositioning.id, mood_id: mood_id)
without manually calling it? I'm thinking like nested forms in a Rails view.
You'll want accepts_nested_attributes_for to do this.
It'll allow you to create related models simply by passing the proper attributes to your API endpoint.

Why can't I create an ActiveRecord entry like this?

I have two associated models:
Class User < ApplicationRecord
has_many :company_accounts
end
Class CompanyAccount < ApplicationRecord
belongs_to :users
end
I want to create a CompanyAccount for an existing user. This works:
#user.company_accounts.create
Why doesn't this work?
CompanyAccount.create(user_id: #user.id)
The full error message is "Users must exist". I'm using rails 5.0.1.
Try with belongs_to :user, it has only one user not many.

Is: grandparent.parents.children association chaining not correct in Rails 4?

I'm having trouble figuring out the proper way of retrieving all children of multiple parents through association chaining.
To simplify I have three models:
class Customer < ActiveRecord::Base
has_many :invoices
end
class Invoice < ActiveRecord::Base
belongs_to :customer
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :invoice
end
After creating a few objects I tired to use the example from rails guides (association basics: 4.3.3.4 includes):
Customer.first.invoices.line_items
It returns:
undefined method `line_items' for #<Customer::ActiveRecord_Associations_CollectionProxy
Is grandparent.parents.children not usable?
EDIT
I'm not searching for the grandparent.parents.first.children, but all children of all parents in the collection, rails guides state:
If you frequently retrieve line items directly from customers (#customer.orders.line_items),
As a valid operation, I would like to know if that is a mistake.
FINAL As stated in the comments of the selected answer: in ActiveRecord: scopes are chainable but associations are not.
The customer.invoices.line_items cannot work the way you want to, since the has_many always is linked to a single record. but you can achieve what you want (if I understand correctly) using has_many through
as follows:
class Customer < ActiveRecord::Base
has_many :invoices
has_many :line_items, through: :invoices
end
class Invoice < ActiveRecord::Base
belongs_to :customer
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :invoice
end
and now you can write:
customer.line_items
and it will return all line_items which are connected to a customer's invoices.
Customer.first.invoices.first.line_items
Or if you want all of the data together, you can do something like:
results = Customer.first.invoices.includes(:line_items)
Then you may access data with no DB call, by looping results. For first data ex: results.first.line_items
Hope it helps!
Customer.first.invoices will return an collection (like an array) of invoices. The line_items method isn't defined for a collection, but its defined for an invoice. Try Customer.first.invoices.first.line_items
EDIT - If you always want the orders to include the line items, you can just do:
class Customer < ActiveRecord::Base
has_many :orders, -> { includes :line_items }
end
class Order < ActiveRecord::Base
belongs_to :customer
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :order
end

Get deleted object with paranoid through related activerecord objects

I have 3 models:
class Request < ActiveRecord::Base
acts_as_paranoid
belongs_to :offer
end
class Offer < ActiveRecord::Base
belongs_to :cruise, inverse_of: :offers
has_many :requests
end
class Travel < ActiveRecord::Base
acts_as_paranoid
has_many :offers, inverse_of: :travel
end
Usually I can access Travel object through chain like this: request.offer.travel.
However, if Travel object I need is deleted with paranoia - I can not access it through such objects chain.
Travel.with_deleted.find(some_id_of_deleted_travel) works perfectly, but request.offers.travel.with_deleted, that the same object, throws me undefined method 'with_deleted' for nil:NilClass.
How can I access deleted object through relation?
I've found the answer, however, I'm not satisfied with it.
It order to get associated object that was soft deleted, I have to modify Offer model like this and unscope relation:
class Offer < ActiveRecord::Base
belongs_to :cruise, inverse_of: :offers
has_many :requests
def travel
Travel.unscoped { super }
end
end
In my case this works, but breaks some functionality, cause I need to unscope relation only in this very situation, leaving other cases untouched. It would be nice to have something like request.offers.travel(:unscoped) etc.
In my case best solution was simply access this object separately like Travel.with_deleted.find(#offer.travel_id)
On Rails > 4. You can use the unscope method for remove the paranoid scope.
class Request < ActiveRecord::Base
acts_as_paranoid
belongs_to :offer, -> { unscope(where: :deleted_at) }
end

Polymorphic association fails

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.

Resources