I've got a Brand model which has a lot of assets:
class Brand < ActiveRecord::Base
attr_accessible :name, :logo1, :logo2, :colour1, :colour2, :prices_attributes
has_attached_file :logo1
has_attached_file :logo2
has_many :users
has_many :welcome_messages
has_many :silos
has_many :articles, :through => :silos
has_many :libraries
has_many :covers, :through => :libraries
has_many :products
has_many :prices, :through => :products, :autosave => true, :dependent => :destroy
accepts_nested_attributes_for :prices
end
Is there a quick way to get all the assets assigned to each brand? Or do I have to do something like brand.articles.each do |article| ... for each?
Cheers!
If you want to eager load all the associations you can do the following:
def self.all_associations
includes(:users, :articles, :prices, :products) #and any other association
end
Then you can run Brand.all_associations or Brand.all_associations.find(1)
As jvnill wrote this will result in n database queries, so in the above example 5 database queries.
Further to mind.blank's solution, if you don't want to repeat yourself, you can simplify things a step further:
def self.all_associations
includes *reflect_on_all_associations.collect(&:name)
end
Related
In my Rails 6 app I have these models:
class Account < ApplicationRecord
has_many :carriers
has_many :clients
# Unfortunately, these two don't work together. I have to uncomment one of them to get the other one to work:
has_many :people, :through => :carriers # works but omits clients
has_many :people, :through => :clients # works but omits carriers
end
class Carrier < ApplicationRecord
belongs_to :account
has_many :people, :as => :personalizable
end
class Client < ApplicationRecord
belongs_to :account
has_many :people, :as => :personalizable
end
class Person < ApplicationRecord
belongs_to :personalizable, :polymorphic => true
end
How can I access an account's carriers and clients in one query?
I would love to do something like account.people to show all the account's people but haven't found a way to achieve that yet.
How can it be done?
You cannot use same method name for two associations instead you can rename it as carrier_people and client_people and eager load both.
class Account < ApplicationRecord
has_many :carriers
has_many :clients
has_many :carrier_people, :through => :carriers, source: :people # works but omits clients
has_many :client_people, :through => :clients, source: :people # works but omits carriers
end
You can eager load like this.
Account.includes(:carrier_people, :client_people)
I have 3 models.
class Team < ActiveRecord::Base
...
has_many :seasons_teams, :dependent => :destroy
has_many :seasons, :through => :seasons_teams
has_and_belongs_to_many :players
...
end
class Season < ActiveRecord::Base
...
has_many :players_seasons, :dependent => :destroy
has_many :players, :through => :players_seasons
has_many :seasons_teams, :dependent => :destroy
has_many :teams, :through => :seasons_teams
...
end
class Player < ActiveRecord::Base
...
has_many :players_seasons, :dependent => :destroy
has_many :seasons, :through => :players_seasons
has_and_belongs_to_many :teams
...
end
There will be validations such that any given player can have at most one team for each season.
I am looking for an efficient way to get a players team for any given season, i.e.:
#player.team(#season)
Thanks!
I think something like this ought to work:
#player.teams.joins( :seasons ).where( :season_id => #season.id ).first
To get the nice concise syntax you're looking for you would use it in a scope, e.g.:
class Team < ActiveRecord::Base
scope :for_season, lambda do |season|
joins( :seasons ).where( :season_id => season.id )
end
# ...
end
class Player < ActiveRecord::Base
# ...
def team season
teams.for_season( season ).first
end
end
#player.team #season
# => #<Team:0x...>
It looks like you either have a typo or a mistake in your team class. I think you should have:
has_many :seasons, :through => :seasons_teams
#player.teams.find_by_season_id(#season.id)
I have the following associations in my application:
# user.rb
has_many :posts, :dependent => :destroy
has_many :likes, :dependent => :destroy
# post.rb
belongs_to :user
has_many :likes, :dependent => :destroy
# like.rb
belongs_to :user
belongs_to :post
When I'm trying to access all the posts that a user liked I'm using the following loop
#user = User.find(params[:id])
#posts_user_likes = []
#user.likes.each do |like| # TODO optimize
#posts_user_likes << Post.find_by_id(like.post_id)
end
but that seems very inefficient.
What's the best way to improve my code, either with different association or different way of looping?
Add has_many :liked_posts, :through => :likes, :class_name => 'Post' to User and then call User.find(params[:id]).liked_posts.
I need to write a scope that I will pass a user's id through and will collect all the user's list associates with all the companies for that user from the users table.
In User.rb:
has_many :employments
has_many :companies, :through => :employments, :dependent => :destroy
...
In Employment.rb:
belongs_to :user
belongs_to :company
In Company.rb:
has_many :employments
has_many :users, :through => :employments, :dependent => :destroy
This could be possible using something like:
current_user.companies.each{|c| c.users.each {|u| u}}
but writing like this I think is much more time consuming.
The following (not tested) scope finds all companies where a given user had work.
class Company
has_many :employments
has_many :users, :through => :employments, :dependent => :destroy
scope :with_user, lambda { |user_id| joins(:employments).where(:user => user_id) }
...
Run rails c and execute try Company.with_user(User.last!.id) and see what happens.
I have a Partner model that has_and_belongs_to_many Projects, while each Project has_many Sites. I want to retrieve all sites for a given partner (and am not interested in the projects in between at the moment).
I have accomplished what I need through a named_scope on the Site model, and a project.sites instance method that wraps a call to the Site named scope, as follows:
class Partner < ActiveRecord::Base
has_and_belongs_to_many :projects
def sites
Site.for_partner_id(self.id)
end
end
class Project < ActiveRecord::Base
has_many :sites
end
class Site < ActiveRecord::Base
belongs_to :project
named_scope :for_partner_id, lambda {|partner_id|
{ :include=>{:project=>:partners},
:conditions=>"partners.id = #{partner_id}"
}
}
end
Now, given a partner instance, I can call partner.sites and get back a collection of all sites associated with the partner. This is precisely the behavior I want, but I'm wondering if there's another way to do this using only activerecord associations, without the named scope?
I had a similar deep nesting query/collection problem here (I had to threaten to repeat data before anyone would answer my 4 questions, clever):
Is it appropriate to repeat data in models to satisfy using law of demeter in collections?
The trick is this gem http://rubygems.org/gems/nested_has_many_through which can do something like this:
class Author < User
has_many :posts
has_many :categories, :through => :posts, :uniq => true
has_many :similar_posts, :through => :categories, :source => :posts
has_many :similar_authors, :through => :similar_posts, :source => :author, :uniq => true
has_many :posts_of_similar_authors, :through => :similar_authors, :source => :posts, :uniq => true
has_many :commenters, :through => :posts, :uniq => true
end
class Post < ActiveRecord::Base
belongs_to :author
belongs_to :category
has_many :comments
has_many :commenters, :through => :comments, :source => :user, :uniq => true
end
This has super-simplified my queries and collections. I hope you find an answer to your problem, it's a tough one!