Rails - combine multiple has_many throughs - ruby-on-rails

I'm just started with Rails, and have a problem I can't solve myself:
User.rb:
has_many :bid_listings, through: :bids, source: :listing, uniq: true
has_many :offer_listings, through: :offers, source: :listing, uniq: true
Both of these return listings, and using methods/scopes from Listing model individually works perfectly. However, when I'm trying to combine these, I'm getting an Array, where i can't apply Listing model's methods and scopes.
I've tried multiple ways, but stuck. Please help.
P.S. User has many bids, User has many offers, bid belongs to listing, offer belongs to listing

You're calling an instance method on an Array object, as opposed to on an ActiveRecord object. Therefore, object of type Array has no idea what the search method is. Try this out:
Edit
user = User.first
listings = Listing.joins(:bids).joins(:offers).where(:bids => {:user_id => user.id}, :offers => {:user_id => user.id})
listings.search('a')

I had a similar problem, and the best solution I came up with was something like:
def buying_listings
Listing.find_by_sql(bid_listings.union(offer_listings).to_sql)
end
This way should still allow you continue scoping, but is less efficient as it will execute an extra query.

Related

Filtering on a field in a has_many through association

I'm having trouble finding the best way to filter on a has_many through relationship directly with Active Record. I've found a bunch of posts here that almost address the issue but none of the answers have worked for me.
Group:
has_many :taggings, as: :taggable
has_many :tags, :through => :taggings
User:
has_many :taggings, as: :taggable
has_many :tags, :through => :taggings
I want to find all users who have tags that match a single groups tags
This following method works but I would rather assemble the list with a query.
def interest_matches
matched_users = Array.new
self.tags.uniq.each do |tag|
tag.users.map{ |u| matched_users.push(u) unless matched_users.include?(u) }
end
matched_users
end
Any advice is much appreciated!
I am assuming that you are calling interest_matches on tags within a group of tags,
Minor note: you are calling self.tags.uniq twice, in lines 1 and 3 of the function. Also,
unless matched_users.include?(u)
the above will iterate through the array every single time to check for existence of the user within the array, not the best option! Next time just call the .uniq method on it at the end.
Anyway, to answer your question, you probably want the join table to access the user's tags.
Method , may or may not work(hopefully it does):
#matched = []
group_of_tags = (i'm assuming this is predefined)
#matched << User.joins(:tags).where(tag_name: group_of_tags.pluck(:name))
#matched.uniq
Not 100% on this, but give it a go :)

Has many through associations with conditions

I am trying to add a condition to a has many through association without luck. This is the association in my video model:
has_many :voted_users, :through => :video_votes, :source => :user
I want to only get the voted_users whose video_votes have a value equal to 1 for that video. How would I do this?
I would suggest creating a model method within the video model class
Something like:
def users_with_one_vote
self.voted_users, :conditions => ['value = ?', 1]
end
Then in the controller use video.users_with_one_vote
Then testing is easier too.
Any chance you can change that column name from 'value'. Might give some issues (reserved?).
I'd do this in 2 stages:
First, I'd define the has_many :through relationship between the models without any conditions.
Second, I'd add a 'scope' that defines a where condition.
Specifically, I'd do something like:
class User < ActiveRecord::Base
has_many :video_votes
has_many :votes, :through=>:video_votes
def self.voted_users
self.video_votes.voted
end
end
class VideoVote
def self.voted
where("value = ?", 1)
end
end
class Video
has_many :video_votes
has_many :users, :through=>:video_votes
end
Then you could get the users that have voted using:
VideoVote.voted.collect(&:user).uniq
which I believe would return an array of all users who had voted. This isn't the exact code you'd use -- they're just snippets -- but the idea is the same.
Would
has_many :voted_users, :through => :video_votes, :source => :user, :conditions => ['users.votes = ?', 1]
Do the trick?
I found that defining this method in my model works:
def upvoted_users
self.voted_users.where("value = 1")
end
and then calling #video.upvoted_users does the trick.
The best way to do this without messing with the relations is by crafting a more complex query. Relations is not the best thing to use for this particular problem. Please understand that relations is more a "way of data definition" then a way of "bussiness rules definition".
Bussiness logic or bussiness rules must be defined on a more specifically layer.
My suggestion for your problem is to create a method to search for users who voted on your video only once. something like:
class Video < ActiveRecord::Base
def voted_once()
User.joins(:video_votes).where("video_votes.value == 1 AND video_votes.video_id == ?", this.id)
end
Rails is magical for many things, but complex queries still have to be done in a "SQL" way of thinking. Don't let the illusional object oriented metaphor blind you
As long as we are throwing around ideas, how about using association extensions.
class VideoVote
scope :upvotes, where(:value => 1)
end
class Video
has_many :voted_users, :through => :video_votes, :source => :user do
def upvoted
scoped & VideoVote.upvotes
end
end
end
Then you feel good about making a call with absolutely no arguments AND you technically didn't add another method to your Video model (it's on the association, right?)
#video.voted_users.upvoted

eager loading a small subset of all has_many objects based on conditions

How do I eager-load only some of the objects in a has_many relationship?
Basically, I have four objects: Assignment, Problem, AssignmentUser, and ProblemUser.
#assignment.rb
has_many :problems
has_many :assignment_users
#assignment_user.rb
belongs_to :assignment
belongs_to :user
has_many :problem_users
#problem.rb
belongs_to :assignment
has_many :problem_users
#problem_user.rb
belongs_to :user
belongs_to :problem
belongs_to :assignment_user
attr_accessor :complete #boolean
On a page showing a single assignment, I want to show all of the problems, as well the user's status on each problem, if it exists. (It might not, if this is the first time the user is viewing the page.)
I can't call assignment_user.problem_users and then snake the problems out like so:
-#assignment_user.problem_users.each do |problem_user|
= check_box_tag "problems[#{problem_user.problem.id}]", 1, problem_user.complete
= link_to problem_user.problem.name, assignment_problem_path(assignment_id => problem_user.problem.assignment_id, :id => problem_user.problem_id)
because there might not be ProblemUser entries for every Problem that belongs to an assignment; creating all of those ProblemUser objects whenever someone creates a Problem object would be wasteful, so they're only created on the fly.
What I want is to be able to iterate over the Problems that belong to the particular Assignment, then for each Problem, find a ProblemUser that matches...but without creating an N+1 problem. I could create two arrays, one with all of the problems and one with all of the problem_users, but then I would have to match them up, right? Maybe that's the best way... but any recommendations on best practices are appreciated here.
Try using :include something along the lines of...
#assignment.rb
has_many :problems, :include => :problem_user
has_many :assignment_users
Assuming a field named description in each of the tables assignments, problems, and problem_users the solution should resemble this...
Assignment.find(1).problems.collect { |a| [a.assignment.description, a.description, a.problem_user.description] }

ActiveScaffold: How to create a drop-down select for polymorphic association?

I'm trying to create a drop-down select box for a polymorphic association with ActiveScaffold.
I have:
class Award
belongs_to :sponsorship, :polymorphic => :true
end
class Organization
has_many :awards, :as => :sponsorship
end
class Individual
has_many :awards, :as => :sponsorship
end
While trying to create a select drop-down box in awards_controller
with:
config.columns[:sponsorship].form_ui = :select
I get the following error:
ActionView::TemplateError
(uninitialized constant
Award::Sponsorship)
I'm not sure if it's something I'm not doing right or what I'm trying
to accomplish not directly supported in AS.
Would really appreciate some advice.
I'm not familiar with ActiveScaffold... But, a quick pass in their documentation revealed a section about has_many :through which I am familiar with from ActiveRecords... so for what it's worth, is it possible that your polymorphic associations should be written like this?:
class Organization
has_many :awards, :through => :sponsorship
end
class Individual
has_many :awards, :through => :sponsorship
end
I'm not sure of what you're trying to do, but rails is indeed true when saying that there is no ":sponsorship'.
When polymorphism is used, rails automatically create two columns, in your case : *sponsorship_id* and *sponsorship_type*.
You may want to use one of those.
However, I'm not familiar with ActiveScaffold form_ui, so I cannot help you further.
I am getting this error, but only if I have an instance of Award with no sponsorship (my names are different...). So presumably the OP and follow up posters got past this, but for future readers, make sure you don't create an instance of the dependent model when using a polymorphic association with active_scaffold...

rails named_scope and :source

I am a beginner in Rails,
Can we use :source with named scope?
I am able to use it with has_many and other associations
Thanks
Mark
No, you can't because you don't need.
A named scope is part of the model where defined in.
class Post
named_scope :published, :conditions => { :published => true }
end
However, this doesn't prevent you from using a named scope through an association.
class Category
has_many :posts
end
category.posts # => all posts
category.posts.published # only published posts
If you can use it in a find() call, generally you can use it with a named scope. The parameters for find are itemized in the documentation (http://apidock.com/rails/ActiveRecord/Base/find/class) but I'm not sure that source is one of them. As far as I know, that's for a has_many relationship sort of thing, not for find.
However, named scopes can be applied to relationships, so perhaps that's what you're intending.

Resources