I have an association of has_many :through with tags, taggings and categories
Tags
has_many :taggings
has_many :categories, through: :taggings
Taggings
belongs_to :tag
belongs_to :category
Categories
has_many :taggings
has_many :categories, through: :taggings
When I try to query
tag = Tag.where("name LIKE ?", "#{query}")
tag.categories
there's an error:
Undefined categories
I don't know what the difference when you use find and where because when I used find it works fine. Can you give me the idea why?
where returns an array that contains result. find on the other hand returns an object. Try:
tag = Tag.where("name LIKE ?", "#{query}")
tag.first.categories
Find return an object and where clause returns the array of objects when you need to find one record by where clause then you should use:
tag = Tag.where("name LIKE ?", "#{query}").first
categories = tag.categories if tag
If you only want the first record it's simpler and more elegant to use
Tag.find_by_name(query).categories
Related
When dealing with polymorphic associations, is it possible to have a has_many through query that pulls in ALL available source_types?
My understanding so far is that each source type needs its own query method, as I show here in the Image model
image.rb
has_many :image_tags
has_many :tags, through: :image_tags, source: :taggable, source_type: 'Tag'
has_many :people, through: :image_tags, source: :taggable, source_type: 'Person'
has_many :businesses, through: :image_tags, source: :taggable, source_type: 'Business'
...
tag.rb
has_many :image_tags, as: :taggable
has_many :images, through: :image_tags
image_tag.rb
belongs_to :image
belongs_to :taggable, polymorphic: true
def build_taggable(params)
self.taggable = taggable_type.constantize.new(params)
end
What I'd like to be able to do, however, is create one query method that pulls in all associated records regardless of what source_type they might belong to.
Just thinking out loud, would that likely involve creating some sort of raw SQL join that acts directly on the ImageTags table? Or is there a more Railsy/ActiveRecordy way of approaching it?
UPDATE 20200319:
I've since found a way to aggregate the individual methods together, but this still requires the creation of unique methods.
def taggables
tags + people + businesses
end
This allows for something like
i = Image.first
i.crops.map(&:taggables)
Still not quite an answer, but it's a temporary workaround in the meantime.
You can use below query to get any association that is attached to image_tag:
# get all images with all associations
imgs = Image.includes(image_tags: :taggable)
# to get value polymorphic association regardless of it's source
# taggable may be tags, people or businesses
poly_assoc = imgs.first.taggable # associaiton of first record
# to get the association type
pp pol_assoc.to_s.class.to_s
I have model structure like this:
class Forum < ActiveRecord::Base
has_many :topics
class Topic < ActiveRecord::Base
has_many :posts
belongs_to :forum
class Post < ActiveRecord::Base
belongs_to :topic
has_many :post_links
has_many :links, ->{ uniq }, through: :post_links, dependent: :destroy
class PostLink < ActiveRecord::Base
belongs_to :post
belongs_to :link
class Link < ActiveRecord::Base
has_many :post_links
has_many :posts, ->{ uniq }, through: :post_links, dependent: :destroy
Now I want to get all Links from Forum with id = 1, sorted by how often they show up in posts in this forum.
Link.joins(posts: [topic: :forum]).where("forums.id = ?",1).group("links.id").order("count_all DESC").count
It gives me hash like {140 => 10, 12 => 9, 137 => 8}
I'd like to have Link relation instead of hash with ids, but I'm not sure how to change this query.
Using select in the query should return an ActiveRecord::Relation. Try this:
Link.select("links.*, COUNT(links.id) as link_count").joins(posts: [topic: :forum]).where("forums.id = ?",1).group("links.id").order("link_count DESC").count
Since I don't have your model structure and relations I can't test this, but I did it with a similar query. Using selectyou will get a Relation.
links.* will return all columns/attributes of the Link model. In case you only need specific attributes (like the url of the link) just use link.url instead.
I managed to solve it with this query:
Link.joins(posts: [topic: :forum]).where("forums.id = ?",1)
.group("links.id").order("count_links_id DESC")
.select("links.*, COUNT(links.id) AS count_links_id")
Since you are fetching the count form the maximum grouped ID's you cannot directly get the active record relation but,
You can just fire a where condition in the Link model from the returned result to get the Active record association,
Link.where(id: (Link.joins(posts: [topic: :forum]).where("forums.id = ?",1).group("links.id").order("count_all DESC").count).keys)
Returns,
#<ActiveRecord::Relation [#<Link...]
I have something like the following:
class Group < ActiveRecord::Base
has_many :group_projects, dependent: :destroy
has_many :projects, through: :group_projects
end
class Projects < ActiveRecord::Base
has_many :group_projects, dependent: :destroy
has_many :groups, through: :group_projects
has_many :time_entries
end
class TimeEntry < ActiveRecord::Base
belongs_to :project
end
So, project.time_entries returns an ActiveRecord::Associations::CollectionProxy of time_entries belonging to that project.
What I want is a list of all time_entries associated to all projects associated with a particular group as a single collection without having to do something like:
TimeEntry.where(["project_id IN (?)", #group.projects.map{|p| p.id } ])
PS: Using Rails 4.0.0 | ruby 2.0.0
Per the edge docs on subset conditions, you can pass an array to the conditions hash of a where() query. This will return an ActiveRecord::Relation, rather than an array of objects:
TimeEntry.where(project_id: #group.projects(&:id))
Note the use of the mapping shorthand (the part with the ampersand). It's an alternative and (perhaps) more readable method for returning an array of a single attribute on multiple objects:
#group.projects.map{|p| p.id} == #group.projects(&:id)
In addition to zeantsoi answer, you can also use:
TimeEntry.where(project_id: #group.project_ids)
#group.project_ids will return an array of all the ids for that group.
I hope it helps.
I'm not sure that was possible at the time this question was posted, but right now has_many :through seems like an obvious and semantic choice for this task:
# Group
has_many :time_entries, through: projects
And then you can do:
#group.time_entries
Let's say I have:
class Post
has_many :tags, :through => :taggings
has_many :taggings
end
Notice there's no :include. Now say I want to retrieve all taggings and tags in the same query. How could I do that?
I'm looking for something like:
taggings = post.taggings(:include => tags) # doesn't work
I could make a custom query or add a third association to Post with an :include, but neither feels right.
I think you can use includes on the association proxy as you would with the model class:
taggings = post.taggings.includes(:tag)
I'm writing some tricky polymorphic relationships to handle tagging.
I have a Tag model, and a Tagging model which belongs_to a polymorphic taggable.
I have an Item model, which has_many :taggings, :as => :taggable, and has_many :tags, :through => :taggings, so that I can call #item.tags.
This is all working ok.
I want to bring another model into the mix - a Store which has_many :items. I want to be able to find all tags associated with all items in the store using #store.tags.
Here's what I have:
class Store < AR::Base
has_many :items
has_many :tags, :through => :items, :source => :taggings
However, this returns all of the taggings associated with items in the store, not the actual tags.
How do I get specify that the store has_many tags, through items, through taggings?
Can post more info if needed - trying to prevent information overload! Thanks :)
The source for a has_many association must be a belongs_to, has_one, or has_many association without a :through option (thanks to this answer for info).
There is a plugin that some people have had success with, but in my case it didn't seem to work correctly with my taggable polymorphic association.
At the moment, my solution is to pass a finder_sql option to has_many:
class Store < ActiveRecord::Base
has_many :items
has_many :tags, :finder_sql =>
'SELECT tags.* from tags
INNER JOIN taggings on tags.id = taggings.tag_id
INNER JOIN items on items.id = taggings.taggable_id AND taggings.taggable_type = "Item"
WHERE items.store_id = #{id}'
end
You can do in plain Ruby:
site.wares.map(&:tags).flatten.uniq
This will be inefficient though, unless you have a low number of tags / wares / items.