Get deleted object with paranoid through related activerecord objects - ruby-on-rails

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

Related

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

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.

Rails multiple association types

Can you have a has_many belongs_to relationship AND a has_many :through relationship without any method call conflicts?
For example, what would user.hacks return - the hacks that the user posted, or the hacks that the user marked as favorites?
I need to be able to access both results.
class User < ActiveRecord::Base
has_many :hacks, through: :favorites
has_many :hacks
end
class Hack < ActiveRecord::Base
has_many :hacks, through: :favorites
belongs_to :users
end
class Favorite < ActiveRecord::Base
belongs_to :user
belongs_to :hack
end
The has_many defines the methods depending on the relationship name.
This means, that if you are defining a relationship several times with the same name but different options, the last definition will override the methods of the previous definition calls.
So you can't define them with the same name.
If you need to access to both, hacks and favorite hacks, you have to create the relationships as follows
class User < ActiveRecord::Base
has_many :hacks
has_many :favorites
has_many :favorited_hacks, through: :favorites, source: :hack
end
I'm not sure what it will return, try it in the console.
However i would something like that in the user class (not tested, but you can look on how to alias relations):
has_many :favorite_hacks, through: :favorites, class: :hacks
There seems to be a typo in Hack.
On another note, the way you do it seems strange to me. The user hack relation seems like ownership, and the user - fav seems like bookmarking.
If all you want is to get a list of all favourite hacks (and all hacks belong to the user), then I think it would be a much better idea to model this behaviour like so:
class User < ActiveRecord::Base
has_many :hacks
end
class Hack < ActiveRecord::Base
belongs_to :user
scope :favorite, -> { where(favorite: true) }
end
and simply have a favorite attribute on your Hack model. Then you could find the favorites by invoking user.hacks.favorite.

Filtering results of a nested association based on the count of a deeper association

I have the following models and associations:
class ClassProfile < ActiveRecord::Base
has_many :enrollments, dependent: :destroy
has_many :student_profiles, through: :enrollments
class Enrollment < ActiveRecord::Base
belongs_to :student_profile
belongs_to :class_profile
class StudentProfile < ActiveRecord::Base
has_one :enrollment, dependent: :destroy
has_one :class_profile, through: :enrollment
has_many :relationships
has_many :parent_profiles, through: :relationships
class Relationship < ActiveRecord::Base
belongs_to :student_profile
belongs_to :parent_profile
class ParentProfile < ActiveRecord::Base
has_many :relationships
has_many :student_profiles, through: :relationships
What I want to do is define a method like the one below for ClassProfile
class ClassProfile < ActiveRecord::Base
...
def orphans
#return a collection of all student_profiles where
#there are no parent_profiles associated
#(i.e. parent_profiles.count = 0 or parent_profiles.empty? = true
end
I'd like to do this, if possible, with a single statement where I don't have to write a loop that manually queries each student_profile. Is there a way to do this, and if so, what is it?
Update
To clarify: I do have a method in StudentProfile as shown below:
class StudentProfile < ActiveRecord::Base
def child?
self.relationships[0].present?
end
And so I'd like to use a method like this:
class ClassProfile < ActiveRecord::Base
...
def orphans
self.student_profiles.where( child? == false )
end
That is, a single statement that returns the proper collection. But this is not a valid use of where and throws an error. Which makes sense because as far as I can tell, the where method on an associated model only works on fields, not methods. Anyway, this is the kind of thing I'm looking for, only something that is actually valid.
Maybe you can do a join for this. In the ClassProfile
def orphans
self.joins(student_profiles: :relationships)
end
The join here is an inner join. Students with no relationships will not be joined here. Moreover, maybe you can try a better name for your methods. Your naming is quite ambiguous.
I figured this out. Here's the code that has the desired behavior:
def orphans
student_profiles.reject { |s| s.child? }
end
I believe this needs to iterate through all the student profiles (as opposed to something that works at the query level), but it gets the job done.

Exposing associations of a has_many -> has_many through the parent?

If a user has many things and a thing has many stats, it seems like there's only way Rails-y way to expose the stats through the user.
class User < ActiveRecord::Base
has_many :things do
def stats
Stat.where(thing_id: proxy_association.owner.things_id)
end
end
end
class Thing < ActiveRecord::Base
belongs_to :user
has_many :stats
end
class Stat < ActiveRecord::Base
belongs_to :thing
has_one :user, through: :thing
end
User.first.things.stats == Stat.where(thing_id: User.first.thing_ids)
I'm trying to determine whether there are any other options... Some people on my team complain that this doesn't feel natural. I feel like this is the most natural expression of the relationship you could devise.
Does anyone have a better suggestion? I'll say, I've tried instance methods and they don't smell right.
I might use a has_many :stats, :through => :things in the User class.
Check out this: http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association

Resources