I've implemented polymorphic comments on my Rails app using this tutorial.
So I have:
class Place < ApplicationRecord
has_many :comments, as: :commentable
end
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
end
Now I want to index all comments and join them with the places they comment on, but I have no idea on how to achieve this.
In my comments_controller.rb I have:
class CommentsController < ApplicationController
def index
#comments = Comment.all
end
end
And in my comments index.html.erb I can access:
<%= comment.commentable_type %>
<%= comment.commentable_id %>
But how do I access the actual attributes of what the comments are on, example: <%= comment.place.name %>?
I believe it is related to this answer but I don't know how: rails joins polymorphic association
You can refer to the commentable object with just
comment.commentable
The only thing to watch out for here is that it assumes that all your commentables have the method you call on them, such as name.
comment.commentable.name
You will want to preload these in your controller to avoid an N+1 as well.
class CommentsController < ApplicationController
def index
#comments = Comment.includes(:commentable).all
end
end
mccalljt answer's above is much better
Believe I've got it:
Added to comment.rb:
belongs_to :place, foreign_key: 'commentable_id'
Added to comments_controller.rb:
#comments = Comment.where(commentable_type: "Place").joins(:place)
Though this is not reusable when there are more associations, but I could change it to:
#place_comments = Comment.where(commentable_type: "Place").joins(:place)
#other_comments = Comment.where(commentable_type: "Other").joins(:other)
Related
I tried to make a topic has a :datetime attribute so that I am able to show latest discussed topic on top of topic index page. The :datetime was determined by its post
The relation are as following
class Topic < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :topic
end
I have tried to make #topic.updated_at = #post.updated_at
but it seems not work.
So I add a new attribute disscuss_time: to topic model and make topic model like following:
class Topic < ActiveRecord::Base
has_many :posts
before_save :default_values
def default_values
self.discuss_time ||= self.updated_at
end
end
And in my post_controller
class PostsController < ApplicationController
def create
#topic = Topic.find(params[:topic_id])
#topic.discuss_time = #post.updated_at
end
The Post.new is in Topic controller
And make the view like
<% topics.order("discuss_time desc").each do |f| %>
It is not work. Although the post is showed on topic's show page, in my admin page the diss_time is empty. And in topic's index page, everything is in its default order. How to pass the :datetime attribute form model to model?
There is another way, which is to make the topic updated while topic.post.count changed. But this way seems still have some problem.
class Topic < ActiveRecord::Base
has_many :posts, after_add: :touch
end
This creates a callback on the posts association which calls touch (which updates the created_at timestamp to the current time).
If you really need the time to be EXACTLY the same as the posts created_at you can instead do:
class Topic < ActiveRecord::Base
has_many :posts, after_add: :update_ts!
def update_ts(post)
self.update_attribute(:created_at, post.created_at)
end
end
You don't need to do anything special in the controller:
class PostsController < ApplicationController
def create
#topic = Topic.find(params[:topic_id])
#post = #topic.posts.new(topic_params)
# ...
end
end
I had this working very similar to another controller however i needed to change this relation to another controller called agreements_controller. I want to create a has one model. review has one and belongs to agreements.
Why isn't the row being created properly?
reviews_controller:
class ReviewsController < ApplicationController
def create
#review = Reviews.create(review_params)
end
private
def review_params
params.require(:review).permit(:comment, :star, :agreement_id, :user_id, :reviser_user_id)
end
end
_form.html.erb
<%= form_for([agreement, agreement.build_review] ) do |f| %>
<% end %>
agreement.rb
class Agreement < ActiveRecord::Base
has_one :review, :dependent => :destroy
end
review.rb
class Review < ActiveRecord::Base
belongs_to :agreement
belongs_to :reviser_user
belongs_to :user
end
I've tried to find similar examples online, but all I could find was nested forms... I don't need a nested form I just want the review to create as a has one.
Models are Singular. Use
Review.create(review_params)
I'm working with three tables as follows:
article.rb
class Article < ActiveRecord::Base
has_many :comments
has_many :comentarios, :through => :comments
end
comment.rb
class Comment < ActiveRecord::Base
belongs_to :article
has_many :comentarios
end
and comentario.rb
class Comentario < ActiveRecord::Base
belongs_to :article
end
Everything works fine until I attempt to add a 'comentario' and returns this error
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection in ComentariosController#create
Cannot modify association 'Article#comentarios' because the source reflection class 'Comentario' is associated to 'Comment' via :has_many.
This is the code I use to create a new 'comentario'
comentarios_controller.rb
class ComentariosController < ApplicationController
def new
#comentario = Comentario.new
end
def create
#article = Article.find(params[:article_id])
#comentario = #article.comentarios.create(comentario_params)
redirect_to article_path(#article)
end
private
def comentario_params
params.require(:comentario).permit(:comentador, :comentario)
end
end
The output returns an error in the line where I create #comentario from calling #article but I can't see why since Ruby documentation says that once I associate comentario to article using :through, I can simply call something like #article.comentario.
Any idea of what is causing this error?
or do you have any suggestion on how to achieve this association in any other way?
Ok. The issue is that Rails is confused about which article to use here.
Your Comment model belongs_to :article but also your Commentario belongs_to :article... so if you use #article.commentarios - it's confused as to whether the article refers to the article of the comment or the article of the commentario.
You will probably need to update your form to be more explicit about what you're referring to. A form for the commentario should actually include fields for the comment it creates.
Somebody else had the same problem here. You may wish to look at the solution here: "Cannot modify association because the source reflection class is associated via :has_many"
I want to display count of subscribers for particular speaker in my index page. I didnt do anything in my route because i am simply looping in my index page. I have working listenerspeakers association. In my pages controller i have
class PagesController < ApplicationController
def home
#messages = Message.all.order("created_at DESC")
#speakers = Speaker.all
#sub = ListenersSpeakers.all
end
end
in my view i try to loop and insert this code but it doesnt work.
<% #sub.each do |sub| %>
<%= #sub.speaker.count %> Subscribers</span>
<% end %>
my model
class ListenersSpeakers < ActiveRecord::Base
belongs_to :listener
belongs_to :speaker
end
Update
my Speaker model has
has_many :listeners_speakers, class_name: 'ListenersSpeakers'
So I assume that in the Speaker model, it has_many :listener_speakers like:
class Speaker < ActiveRecord::Base
has_many :listener_speakers
end
You can then use
#sub.speaker.listener_speakers.count
in the view to get the number of listeners.
Not related to the question, but you may also want to consider adding a:
has_many :listeners, through: :listener_speakers
as that may eventually prove useful.
I have users, posts and comments. User can post only one comment to each post.
class User < ActiveRecord::Base
has_many :posts
has_many :comments
end
class Post < ActiveRecord::Base
has_many :comments
belongs_to :user
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
On userpage (http://host/users/1 for example) I want to show all posts where the given user has commented. Each post then will have all other comments.
I can do something like this in my User controller:
def show
#user = User.find(params[:user_id])
#posts = []
user.comments.each {|comment| #posts << comment.post}
end
This way I will find User, then all his comments, then corresponding post to each comment, and then (in my view) for each post I will render post.comments. I'm totally new in Rails, so I can do this =) But I think it's somehow bad and there is a better way to do this, maybe I should use scopes or named_scopes (don't know yet what this is, but looks scary).
So can you point me out to the right direction here?
You could define an association which retrieves all the posts with comments in a single query. Keeping it in the model reduces the complexity of your controllers, enables you to reuse the association and makes it easier to unit test.
class User < ActiveRecord::Base
has_many :posts_with_comments, :through => :comments, :source => :post
# ...
end
:through is an option for has_many to specify a join table through which to perform the query. We need to specify the :source as Rails wouldn't be able to infer the source from :post_with_comments.
Lastly, update your controller to use the association.
def show
#user = User.find(params[:user_id])
#posts = #user.posts_with_comments
end
To understand more about :through and :source take a look at the documentation.
When you got the user, you have the associations to his posts and each post has his comments.
You could write:
(I don't know the names of your table fields, so i named the text text)
# In Controller
#user = User.find(params[:user_id]).include([:posts, :comments])
# In View
#user.posts.each do |post|
post.text
# Comments to the Post
post.comments.each do |comment|
comment.text
end
end
I haven't tested the code, so there could be some errors.