I have three models
Tag => :id, :name
Tagging => :id, :tag_id, :post_id
Post => :id, :summary
I know the id of the tag. I would like to query for all of the posts that have a specific tag_id, through the Taggings model.
Something like
#post = Post.joins(:taggings).where(:tag_id => 17)
but this doesn't work because it is looking for the tag_id in the Post model and not the Tagging model.
I'm not sure how to do this.
I don't like to use string in ActiveRecord queries, so, I prefer this sintax:
#post = Post.joins(:taggings).where(taggings: {tag_id: 17})
First of all :
class Post < ActiveRecord::Base
has_many :taggings
has_many :tags, :through => :taggings
end
class Taggins < ActiveRecord::Base
belongs_to :post
belongs_to :tag
end
class Tag < ActiveRecord::Base
has_many :taggings
has_many :posts, :through => :taggings
end
If you have the tag object you can do
#posts = #tag.posts
or
class Post < ....
....
def self.find_by_tag_id(tag_id)
Post.joins(:taggings).where('taggings.tag_id = ?', tag_id)
end
end
Using the .where format you can pass a string like .where("taggings.tag_id = ?", 17) to qualify the joined taggings table.
As #tharrison mentioned. A solution is:
#post = Post.joins(:taggings).where("taggings.tag_id = ?", 17)
Related
I have following three models
class Rating < ActiveRecord::Base
belongs_to :user
belongs_to :book
end
class User < ActiveRecord::Base
attr_accessible :name, :dob, :mobile
has_many :books, :through => 'ratings'
end
class Book < ActiveRecord::Base
attr_accessible :book_name, :author, :pages
has_many :users, :through => 'ratings'
end
Now I have to find all the "book_name"s of each book which is related the respective user and store it in the array.
Here is code
#book_names = []
#books = Rating.find(:all, 'user_id = ?', current_user.id)
#books.each do |book|
book_info = Book.find(book.id)
#book_names << book_info.book_name
end
Is there any other way for the same or join method.
Yes, there is simple way to do it. Try
#book_names= current_user.books.map(&:book_name)
So the way I did things for these model set ups is a bit different then what you might actually do. How ever I did things like this:
Post Model
class Post < ActiveRecord::Base
belongs_to :blog
belongs_to :user
has_and_belongs_to_many :tags, join_table: 'tags_posts', :dependent => :destroy
has_and_belongs_to_many :categories, join_table: 'categories_posts', :dependent => :destroy
has_many :comments, :dependent => :destroy
validates :title, presence: true
def has_tag?(tag_name)
tags.where(name: tag_name).any?
end
def tag_names
tags.pluck(:name)
end
def tag_names=(names)
self.tags = names.map{ |name| Tag.where(name: name).first }
end
def tag_name=(tag_name)
single_tag = [tag_name]
self.tag_names = single_tag
end
def has_category?(category_name)
categories.where(name: category_name).any?
end
def category_names
categories.pluck(:name)
end
def category_names=(names)
self.categories = names.map{ |name| Category.where(name: name).first }
end
def category_name=(category_name)
single_category_name = [category_name]
self.category_names = single_category_name
end
def user=(id)
user = User.find_by(id: id)
self.user_id = user.id if user
end
end
The above allows us to assign tags and categories and a post to a user (the last part is being refactored out as we speak). You can also get all tags and categories for a post and see if that post has a particular category.
Now what I want to do, in the tags model (for now) is get all the posts that a tag belongs to. But I am not sure how to do that ...
this is my tags model:
Tags Model
class Tag < ActiveRecord::Base
belongs_to :blog
validates :name, presence: true, uniqueness: true
end
How do I accomplish what I want?
I am not sure how to do this with has_and_belong_to_many. However, it would be pretty easy using has many through. By Rails conventions, the same of your join table should be tag_posts or post_tags (the first model is singular).
In your Post model:
has_many :tag_posts
has_many :tags, :through => :tag_posts
Then in your Tag model, a similar setup:
has_many :tag_posts
has_many :posts, :through => :tag_posts
Finally, you would create a TagPost model
belongs_to :tag
belongs_to :post
After that, calling tag.posts should return all posts for a given tag.
I have the following model classes...
class Image < ActiveRecord::Base
attr_accessible :description, :title
has_many :imageTags
has_many :tags, :through => :imageTags
end
class Tag < ActiveRecord::Base
attr_accessible :name
has_many :imageTags
has_many :images, :through => :imageTags
end
class ImageTag < ActiveRecord::Base
attr_accessible :position
belongs_to :image
belongs_to :tag
end
And when I use find for getting the Tag with the id 1
t = Tag.find(1);
#images = t.images;
But when I do the same with where, I get a NoMethodError, with the description undefined method 'images':
t = Tag.where(:name => "foo");
#images = t.images;
I also tried adding .includes(:images) before the .where statement, but that doesn't work too. So, how can I get all Images that belong to a Tag?
.where returns an ActiveRecord::Relation instance, not a single object. Tack on a .first to grab (presumably) the only record returned:
t = Tag.where(name: "foo").first
#images = t.images
I have the following 3 models in my application:
class Submission < ActiveRecord::Base
has_many :linkedsubmissions
end
class Linkedsubmission < ActiveRecord::Base
belongs_to :submission
has_many :lnksubtypes
end
class Lnksubtype < ActiveRecord::Base
belongs_to :linkedsubmission
end
In the code below '#submission.linkedsubmissions.lnksubtypes' is incorrect.
#history = Audit.find(:all, :conditions => ["auditable_id IN (?)",#submission.linkedsubmissions.lnksubtypes.map{|b| b.LSU_ID} ])
I need to find all audits with 'auditable_id' in #submission.linkedsubmissions.lnksubtypes
You need to add the following has_many relationship to your Submission model.
class Submission < ActiveRecord::Base
has_many :linkedsubmissions
has_many :lnksubtypes, :through => :linkedsubmissions
end
Now, you'll be able to reformat your query like this
#history = Audit.find(:all, :conditions => ["auditable_id IN (?)", #submission.lnksubtypes.map(&:LSU_ID) ])
Audit.find(:all, :conditions => ["auditable_id IN (?)",#submission.linkedsubmissions.map{|b| b.lnksubtypes.map{&:LSU_OID) }.flatten ])
But there should be a better way to get those LSU_OID with sql only.
#history = Audit.where(:auditable_id => #submission.lnksubtypes.map(&:LSU_ID))
slightly shorter
i need a little help with a AR query. This is how my models look like:
class User < AR:B
has_many :publications
end
class Publication < AR:B
belongs_to :user
belongs_to :category
end
class Category < AR:B
has_many :publications
end
Now let's say I want to iterate over all existing categories and either display the user's publications, or display something like "#{current_user.name} has no publications in this category".
class PublicationsController < AC:B
def index
#categories = Category.find(:all, :include => :publications, :conditions => { 'publications.user_id' => current_user })
end
end
This gives me all Categories the user actually has publications, but lacks the "empty" ones.
Any suggestions? :-)
This gives you all the Category objects:
#categories = Category.all
Then, if you declare has_many :through associations you can do something like the following:
#categories.each do |category|
if category.users.include?(current_user)
# User has publications
publications = category.publications.select { |pub| pub.user == current_user }
else
# User has no publications
end
end
(has-many-through declarations:
class User < AR:B
has_many :publications
has_many :categories, :through => :publication
end
class Publication < AR:B
belongs_to :user
belongs_to :category
end
class Category < AR:B
has_many :publications
has_many :users, :through => :publication
end
... warning: drycode)
There's probably an neater way to do this using named scopes though.
You might be able to just modify the find call:
#categories = Category.find(:all, :include => :publications, :conditions => [ 'publications.user_id=? OR publications.user_id IS NULL', current_user ])
Notice that we use the Array variant here rather than the Hash variant, since the example in the documentation implies this is the correct usage.