undefined method 'article' - use data from other model - ruby-on-rails

There are articles and comments.
article has_many :comments
comment belongs_to :article
I want to get all comments where value_id is equal to value_id attribute in the article that comment belongs_to.
class Comment < ActiveRecord::Base
belongs_to :article
def self.value_comments
where(value_id: self.article.value_id)
end
end
I get an error:
undefined method `article' for #<Class:0x007fd2a7e46d18>
controller
#value_comments = Comment.value_comments.where(user_id: current_user.id).order("created_at desc")

Having read your question again, my understanding is that you want to find all Comments whose value_id matches the value_id of their associated Article.
Your code is nearly correct - you need a few more parts to get this to work. You need to join your Comment table to your Article table using joins. Then, refer to the column in a where function using arel_table.
So you should end up with something like this:
def self.value_comments
joins(:article).where(self.arel_table[:value_id].eq Article.arel_table[:value_id])
end
You could also consider using sexy_scopes to make it easier to access your columns.

Related

Rails3: Use joined table's data

I'm trying to access my joined table data but alas..
For example association between authors and books, author have many books:
I'm using join funtion to join the author table to the books table in order to access the author's author_name attribute
class Author < ActiveRecord::Base
has_many :books
attr_accessible :author_name
end
class Book < ActiveRecord::Base
belongs_to :author
end
random_author = Author.first
books = random_author.books.where(id > 5).joins(:author) #this should make all author attributes available to each book, right?
book_1 = books.first
book_1.author_name
=> NoMethodError: undefined method `author_name' for #<Book:0x111111>
Of course using the association would work: book_1.author.author_name but that will require another query, which is what I'm trying to avoid.
I mean the joins operation joins the author's data- there must be a way to access it right?
p.s. I can use includes method. and eager load the author data as well, but since I'm just needing a single attribute- is there a way to accomplished it with only a joins method? Thank you
You need to add
.select('books.*, author.name AS author_name')
So your query becomes
books = random_author.books.where(id > 5).joins(:author).select('books.*, author.name AS author_name')

How to go from a relation to a relation of associated model in Active Record

I have often had this sort of situation
class Post
has_many :comments
end
Now if I have a relation of posts, how would I get the relation of all comments on that posts.
What I am looking for is
Post.where(user: user).comments
But this wont work. Am I missing something obvious here? This seems like a common use case.
Basically when you do Post.where(user: user).includes(:comments) we are preloading all the requisite comments already, what I want is to access them directly, without Post.where(user: user).includes(:comments).map{|p| p.comments}.flatten.uniq or something like that..
I'd define a scope
scope :comments_in_user_posts, -> (user) { where(user: user).includes(:comments).map{|p| p.comments}.flatten }
Then use it like Post.comments_in_user_posts(user).
EDIT:
Another option is to use Comment.where(post: Post.where(user: user))
you can also create in Post model static method:
def self.user_comments(user)
Comment.where(post: Post.where(user: user))
end
and then call:
Post.user_comments(user)
If you want to get comment objects, then implement the logic in your Comment class.
You can define the scope like:
class Comment < ActiveRecord::Base
belongs_to :post
scope :from_user, ->(user_id) { joins(post: :user).where(users: {id: user_id} }
end
So that you can call
Comment.from_user(current_user.id)
you need to mention in comment model, belongs_to :post.
class Comment < ActiveRecord::Base
belongs_to :post
end
Now when you are saving any comment related to post. Make sure the #post object is present.
#post.comments.create(name: "New comment")
You can try and check there things in rails console. Now when you see the comment object. It should have the post_id present.
So now
post_obj.comments
give the array of comments and comment.post will give the related post.

How do you associate one model with another?

I have a model called Topic and another called Product.
Here's how the Topic model looks,
class Topic < ActiveRecord::Base
belongs_to :product
end
Topic has columns "title" and "body".
And here's Product,
class Product < ActiveRecord::Base
has_many :topics
end
Product has columns "name" and "desc". Name is unique.
When I create a new Topic, I want the title of Topic to be stored in Name of Product, only if Name doesn't exist yet. If it does, it should not make any change.
But how do I do this?
UPDATE:
User comes to /topics page, enters Title and Body.
What the Topics_controller should do,
1. Read the Title that has been given by the user.
2. Check if that Title already exists in the Products.
3. If it doesn't add it.
4. If it does, don't do anything.
I don't understand how these two models are linked together and how I can access records from the model.
You can achieve this by using one of the callbacks, which ActiveRecord provides.
I'm not sure if I understand your requirements perfectly, so maybe you need to alter the following example:
class Topic < ActiveRecord::Base
belongs_to :product
before_save :add_product_name
private
def add_product_name
self.product.name ||= self.title if Product.find_by(name: self.title).nil?
end
end
You can write a callback like before_create :add_topic_name
Where that method will find product name of topic and assign it to the title of product.
Your requirements are a bit unclear here. Can you specify what your end goal is, from a Behaviour point of view?
If i understand correctly though, why not just overwrite the title method for Topic. This way you are not duplicating data in the DB.
within your Topic class:
class Topic < ActiveRecord::Base
def title
read_attribute(:title) || product.name # will get title of #topic if not nil, else product name
end
end

How to I make my custom mySQL query to in rails 3?

Im trying to display recently added comments from tattoos a user has posted. So If I posted a tattoo, and then user_b posted "hey I like your tattoo" then Im trying to get just the comment.
First of all Im using the acts_as_commentable_with_threading gem which doesnt create a foreign key for the table im trying to join. So my controller cant look for tattoo_id, it has to look for commentable_id
In the controller I would have to call the Comment model and then pass some SQL stuff into it but apparently I have no clue how to pass custom SQL queries into ruby because even tho my query string works in terminal, I get all sorts of nonsense when trying to use it in rails.
Im basically trying to do this:
SELECT comments.id FROM comments,tattoos WHERE commentable_id = tattoos.id AND
tattoos.member_id = #{current_user}
where #{current_user} will be the current_user passed in.
You don't have to jump through so many hoops to accomplish this. acts_as_commentable assigns a polymorphic association, so you should set it up like this:
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :commentable, :polymorphic => true
end
class Tattoo < ActiveRecord::Base
has_many :comments, :as => :commentable
end
class User
has_many comments
end
Then you can access the association as usual:
Tattoo.where(:member_id => current_user).first.comments
See http://railscasts.com/episodes/154-polymorphic-association for a general tutorial on how polymorphic associations work. It just so happens that this railscast uses exactly :commentable as the polymorphic example, so you should be able to follow along directly if you want.
I think Ben's approach is best but for future reference if you do come across something more complicated you can always use sql for example:
Comment.find_by_sql("SELECT comments.* FROM comments,tattoos WHERE commentable_id = tattoos.id AND tattoos.member_id = ?", current_user)

How to order by in Rails?

I'm working on a small blog engine.
There are the following tables: Blog and Message.
Blog has a foreign key: last_message_id, so I access the last message in the blog by calling blog.last_message
I have the following code to make it work:
class Blog < ActiveRecord::Base
belongs_to :last_message, :class_name => "Message"
end
I need to order the blogs by the last messages. But when I call
blogs.order("last_message.created_at DESC")
It doesn't work. I get the following error:
PGError: ERROR: missing FROM-clause entry for table "last_message"
ORDER BY last_messa...
How can I make it work?
UPDATE
Here's the solution:
blogs.joins(:last_message).order("messages.created_at DESC").
I think your model is wrong. See rails automaticly add 2 attributes to a model : created_at and update_at. So having a relationship like you describe is redondant. To me, it should look like this :
#model/blog.rb
class Blog < ActiveRecord::Base
has_many :messages
end
#model/message.rb
class Message < ActiveRecord::Base
belongs_to :blog
end
Then, to get the blogs ordered by the last message, you could do this :
Blog.joins(:messages).order("messages.created_at_desc")
That as you may have noticed will give you double entries for your blog model. If that is not a problem, go ahead. If it is, you have two options : doing a each and test if you already saw the blog - if not, you display it. Or, you could write your own sql.
You have to make sure the last-messages are also selected to make that order-command work.\
So something like:
blogs.includes(:last_message).order("last_message.created_at desc")

Resources