Rails: multiple associations query - ruby-on-rails-4

I have associations like this: Profession -> orders -> profile -> location
Class Profession < ActiveRecord::Base
has_many: orders
end
Class Order < ActiveRecord::Base
has_one :profession
belongs_to :profile
end
Class Profile < ActiveRecord::Base
has_one :location
has_many :orders
end
Class Location < ActiveRecord::Base
belong_to :profile
end
And I need to find a professions which are in the location.city.
For example I try this:
Profession.joins(:orders).where(orders: {profile: {location: {city: "Simferopol"}}})
Is this possible?
Thanks.

You might consider an association using through:
class Profession < ActiveRecord::Base
has_many :orders
has_many :profiles, through: :orders
end
This may make life easier for you allowing you to call:
Profession.profiles
This will return all profiles for a given state. It just seems less messy to me. As you still need to reach the location associated with a profile I am sure there is a more optimal solution, but I believe this approach is better than the proposed:
Profession.joins(:orders).where(orders: {profile: {location: {city: "Simferopol"}}})

Related

Can a model have two associations to the same model, using :through?

Let's say I have three models: Organization, Skills and Assessments.
Can an Assessment belong to two different Organizations, via different relations?
For example, an assessment may have happened at organization A, but was based on a skill belonging to organization B.
Below are my models and associations:
class Organization < ActiveRecord::Base
has_many :checklists
has_many :levels, :through => :checklists
has_many :sections, :through => :levels
has_many :skills, :through => :sections
has_many :assessments_using_own_checklists, :through => :skills, :source => :assessments
end
class Skill < ActiveRecord::Base
belongs_to :section
has_one :level, through: :section
has_one :checklist, through: :level
has_one :organization, through: :checklist
has_many :assessments
end
class Assessment < ActiveRecord::Base
belongs_to :skill
has_one :section, through: :skill
has_one :level, through: :section
has_one :checklist, through: :level
has_one :checklist_owner, through: :checklist, source: :organization
belongs_to :organization
end
Using the above, I can get an assessment's organization:
Assessment.last.organization # yields organization 1
I can also get an assessment's checklist_owner:
Assessment.last.checklist_owner # yields organization 2
But when I try to use checklist_owner in a where, the association seems to forget to use the :through. For example, if I run:
Assessment.where(organization: Organization.find(2), checklist_owner: Organization.find(1))
... this translates to SQL:
SELECT "assessments".* FROM "assessments" WHERE "assessments"."organization_id" = 2 AND "assessments"."organization_id" = 1
See how the SQL has two "assessments"."organization_id" = statements? Why does it do that?
Have you tried using joins?
something like:
Assessment.joins(skill: { section: { level: :checklist } }).where(organization: Organization.find(2), checklists: { organization_id: Organization.find(1) })
I know it look bad, but it seems that your relation from assessment to checklist is very complicated. This would take care of any weird relations being made.

use of includes or left outer joins for complex nested associations

I have four models
class Company < ActiveRecord::Base
has_many :share_types
belongs_to :user
end
class ShareType < ActiveRecord::Base
has_many :shares
belongs_to :company
end
class Share < ActiveRecord::Base
belongs_to :user
belongs_to :share_type
end
class User < ActiveRecord:Base
has_many :companies
has_many :shares
end
Now list of all companies where company is owned by current_user or user have shares in a company something like this.
Company.joins(share_types:[:shares]).where("shares.user_id=? OR companies.user_id=?", #user.id, #user.id)
but with left outer join another I do not know how to use includes with or conditions another hint is
Company.includes(share_types:[:shares]).where(shares:{user_id: #user.id} OR companies:{user_id: 1})
How can I do that.
I am able to get my expected result with the help of references. Here is my query just posting to help others.
Company.includes(share_types:[:shares]).where("shares.user_id=? OR companies.user_id=?", 1,1).references(:shares)
its working thanks to The Rails 4 Way by Obie Fernandez

Getting a count value between many different models and associations

I've been wondering what is the easiest way of getting a count value between several different models and associations.
I want to have something like this in my view
shop.receipts.articles.complaints.complaint_reviews.count
Here are my models and associations between them:
class Shop < ActiveRecord::Base
has_many :receipts
end
class Receipt < ActiveRecord::Base
has_many :articles, dependent: :destroy
belongs_to :shop
accepts_nested_attributes_for :articles, allow_destroy:true, :reject_if => :all_blank
end
class Article < ActiveRecord::Base
belongs_to :receipt
has_one :complaint
end
class Complaint < ActiveRecord::Base
belongs_to :article
has_many :complaint_reviews
end
class ComplaintReview < ActiveRecord::Base
belongs_to :complaint
end
I'm inferring that you want the count of all complaint_reviews that are associated with a particular shop.
In that case, the following is what you need:
shop = # get shop according to your criteria
ComplaintReview.
joins(complaint: {article: {receipt: :shop}}).
where(shops: {id: shop.id}).
count
I suppose you could save the shop joins, by applying the condition on the shop_id column of receipts; like so:
ComplaintReview.
joins(complaint: {article: :receipt}).
where(receipts: {shop_id: shop.id}).
count
Result should be the same for both if all receipts have a shop associated. But I'd opt for the first method.
The thing to keep in mind here is to 'start' with the model of which you ultimately want the count of.
Also, had there been any one-to-many relationships, you would have grouped the results by "complain_reviews.id" and then performed the count.
Ok so thanks to the code above I managed to come up with a working solution:
#shops_controller.rb:
def show
#count = ComplaintReview.
joins(complaint: {article: {receipt: :shop}}).
where(shops: {id: #shop.id}).
count
respond_with(#shop)
end
#shops/show.html.erb:
<%= #count %>
Thanks a lot for the help.

How to do a Polymorphic model association with another model

I have two basic models:
class Case < ActiveRecord::Base
has_many :contacts
end
class Contact < ActiveRecord::Base
belongs_to :case
belongs_to :contactable, :polymorphic => true, :foreign_key => :contactable_id
end
I also have quite a few models that are "sub types" of the Contact model:
class Attorney < ActiveRecord::Base
has_one :contact, as: :contactable, dependent: :destroy
accepts_nested_attributes_for :contact
end
class Client < ActiveRecord::Base
has_one :contact, as: :contactable, dependent: :destroy
accepts_nested_attributes_for :contact
end
I used polymorphic associations instead of STI because I don't want one table to store all the fields, which can get a little slow and overwhelming.
I am wondering if the has_many :contacts line from the Case model is correct. I try to use it in my console, but it doesn't really work the way I expected. Am I missing something?
I basically want an association where a Case can have many Contacts and a contact can belong to many cases. The contact can be of any type (Attorney or Client or Contact by itself). I was hoping it was as easy as case has_many :contacts

rails scope through has_many association

I have two models for Show and Performance (show as in a play or comedy show). They are associated like this in the models:
class Show < ActiveRecord::Base
has_many :performances, :dependent => :destroy
accepts_nested_attributes_for :performances
end
class Performance < ActiveRecord::Base
belongs_to :show
end
In the Performance model there is a datetime called :start_time.
How do I define a scope in the model which returns all Shows with at least one performance whose :start_time is in the future?
Also, how do I define a scope that returns all Shows that do not have any performances in whose :start_time is in the future?
class Show < ActiveRecord::Base
has_many :performances, :dependent => :destroy
accepts_nested_attributes_for :performances
scope :shows_with_pending_performance, includes(:performances).where("performances.start_time >= ? ", Date.today)
end
class Performance < ActiveRecord::Base
belongs_to :show
end