Optimizing rails query and associations - ruby-on-rails

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.

Related

after_destroy not called for linked table

I have these models
class User < ActiveRecord::Base
has_many :user_functions, :dependent => :destroy
has_many :functions, :through => :user_functions
accepts_nested_attributes_for :functions, allow_destroy: true
Model of the linked table:
class UserFunction < ActiveRecord::Base
belongs_to :user, inverse_of: :user_functions
belongs_to :function, inverse_of: :user_functions
after_destroy :unplan_items
after_create :plan_items
and of course the model of function but this is like user...
Now when I do the following in my tests:
#user.functions = [#functions]
#user.save
expect(#user.planned_items.count).to eq(1)
#user.functions = []
#user.save
I notice the callback after_destroy isn't called. Why is this and how can I avoid this. There are certain steps that need to be done every time a UserFunction is destroyed...
I believe this has to do with: https://github.com/rails/rails/issues/7618 (I'm using rails 4.2.5 though). The after_create is working perfect though...
Currently rails uses :delete_all as default strategy of has_many_through. It only calls :destroy_all when we explicitly specify dependent: :destroy on the association.
The docs mention advice to use has_many :through if you need callbacks:
See the suggestion here: http://guides.rubyonrails.org/association_basics.html
You should use has_many :through if you need validations, callbacks,
or extra attributes on the join model.
So there currently is an inconsistency between after_create which does do the callback and after_destroy.
This is mentioned in these two issues posted on GitHub:
https://github.com/rails/rails/issues/7618
https://github.com/rails/rails/issues/27099
The fix for now is to explicitly put :dependent => :destroy on the :through part. This will make sure the callback are used.
class User < ActiveRecord::Base
has_many :user_functions
has_many :functions, :through => :user_functions, :dependent => :destroy
accepts_nested_attributes_for :functions, allow_destroy: true
For anyone reading this 2021+
Change This
has_many :object_tags, :as => :taggable, :dependent => :destroy
has_many :tags, :through => :object_tags
To This
has_many :object_tags
has_many :tags, :through => :object_tags, :dependent => :destroy

Rails: get all assets belonging to a model

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

How do I write a scope for some object from a many to many relationship?

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.

Trying to design a controller to display courses (Rails)

Below are the models that are relevant to my problem. I am attempting to design a way to display CourseEnrollments along with their steps to a given patient. Here is what I have come up with so far.
INDEX ACTION - /course_enrollments/ --> Display courses user is enrolled in as well as the most recent course overview. This can redirect to most recent course.
SHOW ACTION - /course_enrollments/:id --> Display courses user is enrolled in as well as the most recent course overview
The part I am struggling to figure out is how to display an individual step for a course. Should this be done in the course_steps controller (which is nested inside the courses resource)?
class Course < ActiveRecord::Base
belongs_to :course_category
belongs_to :client
belongs_to :user_created, :foreign_key => :user_created_by, :class_name => "User"
belongs_to :user_updated, :foreign_key => :user_last_updated_by, :class_name => "User"
has_many :course_steps, :dependent => :destroy
has_many :steps, :through => :course_steps
has_many :course_requests, :dependent => :destroy
has_many :course_enrollments, :dependent => :destroy
has_many :patients, :through =>:course_enrollments
end
class CourseStep < ActiveRecord::Base
belongs_to :step
belongs_to :course
validates_uniqueness_of :step_id, :scope => :course_id
end
class Step < ActiveRecord::Base
belongs_to :step_type
belongs_to :client
has_one :step_quiz, :dependent => :destroy
has_one :step_survey, :dependent => :destroy
has_one :step_text, :dependent => :destroy
has_one :step_download, :dependent => :destroy
has_one :step_video, :dependent => :destroy
has_one :step_presentation, :dependent => :destroy
has_many :course_steps, :dependent => :destroy
has_many :courses, :through => :course_steps
end
class CourseEnrollment < ActiveRecord::Base
belongs_to :patient
belongs_to :course
end
class Patient < ActiveRecord::Base
belongs_to :user, :dependent => :destroy
has_many :enrollments, :dependent => :destroy
has_many :clients, :through => :enrollments
has_many :course_requests, :dependent => :destroy
has_many :course_enrollments, :dependent => :destroy
has_many :courses, :through => :course_enrollments
end
The usual approach is to nest these and have a compound sort of path, but how you route these things is often dependent on the level of context involved. For instance, is the display of a course driven by a user to the degree that the path should have the user in it, or is it a case of simply personalizing the course page?
Typically you see things like this:
resources :courses do |course|
course.resources :enrollments
course.resources :steps
end
There's usually a correlation between your has_many relationships and declaring an equivalent resources but not always.
Have a look at the generated routes using rake routes to see what the parameters will be called when passed to your controller, as well as what the expected controller name will be. You can customize the name of the controller by passing in a :controller option to the route.
Typically the last record in the path is passed in as :id whereas the prior ones are named, such as :course_id. This is a somewhat annoying inconsistency, so do be careful to check you're loading using the correct parameters.
If you need to display an individual step for a course, you definitely need to use the course_steps controller, logic being, each CourseStep object is a combo of one-course and one-step.

Rails relation select

I have the following models:
class User < ActiveRecord::Base
has_many :results, :dependent => :destroy
has_many :participants, :dependent => :destroy
has_many :courses, :through => :participants
end
class Course < ActiveRecord::Base
has_many :tests, :dependent => :destroy
has_many :participants, :dependent => :destroy
has_many :users, :through => :participants
end
class Result < ActiveRecord::Base
belongs_to :test
belongs_to :user
end
class Test < ActiveRecord::Base
belongs_to :course
has_many :results, :dependent => :destroy
end
The Idea is that a user has_and_belongs_to_many courses, the course has_many tests, and every test has_and_belongs_to_many users (results).
So what is the best query to select every Result from a single Course (not test), and also the query to select every Result from a single Course, but from one user.
Thanks!
To get the results from a specific course - given that the only bridge between the two is the test model you will need to include the test in the query.
Result.find(:all, :conditions => ["tests.course_id = ?",#course.id], :include => :test)
For the second query:
Result.find(:all, :conditions => ["user_id = ? AND tests.course_id = ?",#user.id, #course.id], :include => :test)

Resources