I'm trying to build a method that pulls an array of arrays, much like nested loops in the view.
I am trying to build a method on my User model that does this:
#past_parties = User.parties
<%= #past_parties.each do |past_party| %>
<%= past_party.guests("email").uniq.each do |guest| %>
<%= guest.name %> <%= guest.email %>
<% end %>
<% end %>
These are my models:
class User < ActiveRecord::Base
has_many :hosts, dependent: :destroy
has_many :parties, through: :hosts
def past_guests
self.parties.guests
end
end
class Host < ActiveRecord::Base
belongs_to :user
has_many :parties, dependent: :destroy
has_many :guests, through: :parties
end
class Party < ActiveRecord::Base
belongs_to :host
has_many :guests, dependent: :destroy
end
class Guest < ActiveRecord::Base
belongs_to :party
end
I can get the loop to work in the view, but when I try to build it as a method (so I can call current_user.past_guests, if current_user is an instance of User.), I get this error
undefined method `guests' for #<ActiveRecord::Associations::CollectionProxy []>
How do I get all of the guests that a user has ever invited without building the loop in the view?
The problem is that you're trying to access an array from another array:
self.parties.guests
self.parties returns an #<ActiveRecord::Associations::CollectionProxy []>, so if you want to get the guests of the parties you have to loop over the elements.
But since you want only the guests, you can simply change your user class to:
class User < ActiveRecord::Base
has_many :hosts, dependent: :destroy
has_many :parties, through: :hosts
has_many :guests, through: :parties
# Call your guests
def past_guests
self.guests
end
end
Each party is going to have collection of guests. You need to do this in your method definition:
def past_guests
self.parties.collect(&:guests)
end
Related
I want users to be able to view a task's submit button if and only if they have completed the task's required courses. Right now, I added a line, current_user.complete(#task.courses.all?) to my tasks/show.html.erb page that should only allow a user to view the submit button if they have completed the task's courses. But this line throws an error at the def complete user method saying that the method lessons is undefined for true:TrueClass.
Task Model:
has_many :submissions
has_and_belongs_to_many :courses
Submission Model:
belongs_to :user
belongs_to :task
Course Model:
has_many :lessons, dependent: :destroy
has_many :users, through: :enrolments
has_and_belongs_to_many :tasks, optional: true
Lesson Model:
belongs_to :course
has_many :views
has_many :users, through: :views
User Model:
has_many :courses, through: :enrolments
has_many :submissions
has_many :views
has_many :lessons, through: :views
def view(lesson)
lessons << lesson
end
def viewed?(lesson)
lessons.include?(lesson)
end
def complete(course)
lessons.where(course: course).ids.sort == course.lessons.ids.sort
end
Task/Show.html.erb:
<% if current_user.complete(#task.courses.all?)%>
<%= link_to "Submit", new_task_submission_path(#task), class: "btn btn-primary" %>
<% end %>
Your complete method expects course as a parameter, and it tries to call course.lessons on it.
You are calling
current_user.complete(#task.courses.all?)
which means you are passing a boolean into complete instead of a course.
Perhaps you meant something like:
#task.courses.all? { |course| current_user.complete(course) }
Aleksei Matiushkin suggests that it would be more efficient to use something like:
current_user.joins(:courses).joins(:lessons).where(complete: false).count == 0
I have FamilyTree, Node, Comment, & User models.
The relationship is like this:
FamilyTree
class FamilyTree < ActiveRecord::Base
belongs_to :user
has_many :memberships, dependent: :destroy
has_many :members, through: :memberships, source: :user, dependent: :destroy
has_many :nodes, dependent: :destroy
end
Node
class Node < ActiveRecord::Base
belongs_to :family_tree
belongs_to :user
has_many :comments, dependent: :destroy
end
Comment
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :node
end
User
class User < ActiveRecord::Base
has_one :family_tree, dependent: :destroy
has_many :memberships, dependent: :destroy
has_many :nodes, dependent: :destroy
has_many :comments
end
Membership ## This is just to store the user memberships on various family_trees
class Membership < ActiveRecord::Base
belongs_to :family_tree
belongs_to :user
end
In my Dashboard#IndexController where I am using this, I have:
def index
#family_tree = current_user.family_tree
#nodes = #family_tree.nodes.includes(:comments)
#node = current_user.nodes.new
#memberships = current_user.memberships.limit(3)
end
When I am trying to optimize my app with the Bullet gem, I get this message:
N+1 Query detected
Comment => [:user]
Add to your finder: :include => [:user]
N+1 Query method call stack
My _comments partial that is generating this N+1 issue is called like this - in my views/dashboard/index.html.erb:
<% #nodes.each do |node| %>
<%= render partial: "shared/comments", locals: {node: node} %>
<% end %> <!-- node -->
This is where the n+1 offending queries occur, around these lines in my _comments partial.
<% node.comments.each do |comment| %>
<li class="clearfix">
<a class="avatar" href="#">
<%= image_tag(comment.user.avatar.url)%>
So it seems the solution is to optimize my controller call, but I am not quite sure how to do 2-levels of association. I tried this:
#nodes = #family_tree.nodes.includes(:comments).includes(:user)
But that doesn't seem to get rid of the N+1 query problem.
Any ideas?
You have to pass a hash to the includes
#nodes = #family_tree.nodes.includes(:comments => :user)
There has to be a better way to do this. My Favorite model belongs to User while Applicant belongs to both Gig and User. I am trying to efficiently determine whether a user has applied for Gig that was favorited (<% if #application.present? %>).
I tried chaining the collection by using something like #favorites.each.gig to no avail. While the below index action for Favorites seems to work, it's really verbose and inefficient. What is a more succinct way of doing this?
def index
#favorites = Favorite.where(:candidate_id => current_candidate)
#applications = Applicant.where(:candidate_id => current_candidate)
#favorites.each do |favorite|
#applications.each do |application|
if favorite.gig.id == application.id
#application = application
end
end
end
end
class User
has_many :applicants
has_many :gigs, :through => :applicants
has_many :favorites
end
class Favorite < ActiveRecord::Base
belongs_to :candidate
belongs_to :gig
end
class Applicant < ActiveRecord::Base
belongs_to :gig
belongs_to :candidate
end
class Candidate < ActiveRecord::Base
has_many :applicants
has_many :gigs, :through => :applicants
has_many :favorites
end
class Gig < ActiveRecord::Base
belongs_to :employer
has_many :applicants
has_many :favorites
has_many :users, :through => :applicants
end
For lack of a better answer, here's my idea:
--
User
Your user model should be structured as such (I just highlighted foreign keys, which I imagine you'd have anyway):
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :applicants
has_many :gigs, :through => :applicants, foreign_key: "candidate_id"
has_many :favorites, foreign_key: "candidate_id"
end
This means you'll be able to call:
current_candidate.favorites
current_candidate.applicants
This will remove the need for your #applications and #favorites queries
--
Favorite
You basically want to return a boolean of whether applicant is part of the favorite model or not. In essence, for each favorite the candidate has made, you'll be able to check if it's got an application
I would do this by setting an instance method on your favorites method using an ActiveRecord Association Extension, like so:
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :favorites do
def applied?
self.applicant.exists? proxy_association.owner.gig.id
end
end
end
This will allow you to call:
<%= for favorite in current_candidate.favorites do %>
<%= if favorite.applied? %>
<% end %>
This is untested & highly speculative. I hope it gives you some ideas, though!
I basically followed the ROR guide, http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association, to create the relationship models as shown below.
Because of the through association, I figured that #user.trips would give you both the trips that the user created and the trips that belong to the user. However, when I do #user.trips.count in console, the result was only the number of trips that the users created; the trips that belonged to the user through the 'group' association was not counted.
Question: How do I get my view to display both the trips that the user created and the trips that the user belongs to through 'group'?
user/show.html.erb
<% unless #user.all_trips.empty? %>
<% #user.all_trips.each do |trip| %>
<!-- Content -->
<% end %>
<% end %>
user.rb
class User < ActiveRecord::Base
has_many :group_trips, :through => :groups,
:source => :trip
has_many :trips, :dependent => :destroy
has_many :groups
def all_trips
self.trips | self.group_trips
end
end
trip.rb
class Trip < ActiveRecord::Base
belongs_to :user
belongs_to :traveldeal
has_many :groups
has_many :users, :through => :groups
end
group.rb
class Group < ActiveRecord::Base
belongs_to :trip
belongs_to :user
end
Thanks!
Edit: Modified code per TSherif's partial solution.
Edit 2: Fixed up the all_trips method. Everything appears to work for me at this point.
Oh! I think I get what you're trying to do and why it's a problem. I was wondering why has_many :trips was called twice. But from what I understand, you have two different User-Trip relationships. These two can't have the same name, otherwise one will hide the other. Try something like this:
class User < ActiveRecord::Base
has_many :group_trips, :through => :groups,
:class_name => "Trip"
has_many :trips, :dependent => :destroy
has_many :groups
def all_trips
Trip.joins(:groups).where({:user_id => self.id} | {:groups => {:user_id => self.id}})
end
end
Or if you're using an older version of Rails that doesn't have MetaWhere:
def all_trips
Trip.joins(:groups).where("(trips.user_id = ?) OR (groups.user_id = ?)", self.id, self.id)
end
I have the following models:
class Label < ActiveRecord::Base
has_many :releases
end
class Release < ActiveRecord::Base
belongs_to :label
has_many :products
has_and_belongs_to_many :tracks
def self.releases_count
self.count(:all)
end
end
class Product < ActiveRecord::Base
belongs_to :release
has_many :releases_tracks, :through => :release, :source => :tracks
has_and_belongs_to_many :tracks
def self.products_count
self.count(:all)
end
end
On my label/index view i'm able to display a count of the Releases absolutely fine using:
<%= label.releases.releases_count %>
I'm trying to do the same for Products using:
<%= label.releases.products.products_count %>
But get a NoMethodError:
undefined method `products' for #<Label:0x10ff59690>
Any ideas?
I have lots of other aggregations I want to perform (Track Counts etc) so some guidance on where I'm going wrong would be really appreciated.
You need define your production/Label association
class Label < ActiveRecord::Base
has_many :releases
has_many :products, :through => :releases
end