Nested associates the rails way - ruby-on-rails

I'm fairly new to Rails and I'm trying to gain a better understanding of how to be leverage the Rails framework for my associations.
While it's not specific for my app, the structure is similar -- for my example, I'll use the standard blog associates.
Example Models:
class Author < ActiveRecord::Base
has_many :posts, :through => :posts
end
class Post < ActiveRecord::Base
belongs_to :author
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
end
My challenge is that I want to select all comments that belong to a specific author. I understand how to reference post and author elements that are associated with a specific post:
comment_author = Comment.first
puts comment_author.post.author.name
But as I stated I'm trying to select all comments that belong to a specific author. I could accomplish this by doing a find_by_sql, but I want to ensure db independence and I want to do this the "Rails way."
Thanks!

You can use the has_many :through assocication:
class Author < ActiveRecord::Base
has_many :posts
has_many :comments, :through => :posts
end

#Femaref provided the exact answer to your question and you should accept it. Mine is merely a complement.
If an author can leave comments on posts, here is what you might want to do:
class Author
has_many :posts
has_many :comments
has_many :responses, through: :posts, source: :comments
end
class Post
belongs_to :author
has_many :comments
end
class Comment
belongs_to :author
belongs_to :post
end
To get all the comments left by sophia: sophia.comments
To get all the comments left on sophia's posts: sophia.responses
See the options for has_many (especially through and source)

Related

multiple belongs_to models in Rails

I have a comments model that is currently working with Articles. I would now like to have users be able to comment on the Coffeeshop reviews. Am I able to use the same comment table, or should I have a separate one (feels janky). I've not long been building with RoR (a few weeks) so still trying to get the hang of the basics.
Would I nest them in routes.rb (and how)
resources :coffeeshops do
resources :articles do
resources :comments
end
or
resources :coffeeshops do
resources :comments
end
resources :articles do
resources :comments
end
My models look like:
User
class User < ApplicationRecord
has_many :comments
end
Comments
class Comment < ApplicationRecord
belongs_to :user
belongs_to :article
belongs_to :coffeeshop
end
Articles
class Article < ApplicationRecord
has_many :comments, dependent: :destroy
end
Coffeeshops
class Coffeeshop < ApplicationRecord
has_many :comments, dependent: :destroy
I'm then assuming I need a foreign key to tie the user and comments together, and then also the comments to the article/coffeeshop.
I'd use a polymorphic association.
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
class User < ApplicationRecord
has_many :comments
end
class Comment < ApplicationRecord
belongs_to :user
belongs_to :commentable, polymorphic: true
end
class Article < ApplicationRecord
has_many :comments, as: :commentable
end
class Coffeeshop < ApplicationRecord
has_many :comments, as: :commentable
end
For some more information about setting up the routes/controller:
https://rubyplus.com/articles/3901-Polymorphic-Association-in-Rails-5
http://karimbutt.github.io/blog/2015/01/03/step-by-step-guide-to-polymorphic-associations-in-rails/
You can use comment model for both comments for articles and coffeeshops, but (because by default rails uses ids as primary and foreign keys I assume you use ids too) you will have to add column to comments table, where you set the comment type (You can create Enumerator in comment model, where you set 2 possible value types, each for article and coffeeshop models). If you don't add the column it will result in weird, hard to track bug where you can see comments for article on coffeeshop with same id and vise-versa.
UPD: he's little guide on using enums for rails models: http://www.justinweiss.com/articles/creating-easy-readable-attributes-with-activerecord-enums/ you will have to use it not in actual add comment form, but behind the scenes.

Rails model association

I am trying to design model associations in rails
that consist of the following 3 types:
Commentator, Blogpost and Comment
--> it's "Commentator" and not "User" what means
that they are not the users who create blogposts...
instead they create comments only.
While the basic relationship between Commentator
and Comment is obvious:
class Commentator < ActiveRecord::Base
has_many :comments
class Comment < ActiveRecord::Base
belongs_to: comments
I am not sure how to relate "Blogpost" to this...
--> I would like go be able to ask for all the Blogposts
a Commentator has left as well as all the Commentators
of a specific Blogpost.
Since this is a many-to-many relationship I
would use:
class Commentator < ActiveRecord::Base
has_many :comments
has_many :blogposts, :through => :comments
class Blogpost < ActiveRecord::Base
"has_many :commentators, :through => :comments
When a commentator creates a blogpost, do I have to
write the commenentator_id and blogpost_id in comments
by myself into the corresponding fields of the comment table?
I think it would be better to have Blogposts as the
going through element since the relationship could be
automatically be build when a commentator creates a comment.
(apart from the fact that commentators cannot create comments
to Blogposts that do not exist...)
But then, Commentator to Comment would NOT be a many-to-many
relationship and I cannot use "has_many ... through" anymore.
What is a good way to relate this 3 types of models?
Solution to the stated problem
class Commentator < ActiveRecord::Base
has_many :comments
has_many :blogposts, :through => :comments
end
class Comment < ActiveRecord::Base
belongs_to :commentator
belongs_to :blogpost
end
class Blogpost < ActiveRecord::Base
has_many :comments
has_many :commentators, :through => :comments
belongs_to :user
class User
has_many :blogposts
end
To add a comment to an existing blog post (assuming we have a blog and commentator variables)
blog.comments.create(:commentator => commentator, :comment => "foo bar")
OR
commentator.comments.create(:blog => blog, :comment => "foo bar")
Note
Instead of using two models for users(i.e. User and Commenter), I would use one model and assign privileges
to distinguish between a commentor and a blog post writer.
class User
has_many :blogs
has_many :comments
has_many :commented_blogs, :through => :comments, :source => :blog
end
class Blog
has_many :comments
belongs_to :user
has_many :commenters, :through => :comments, :source => :user
end
class Comment
belongs_to :user
belongs_to :blog
end
Creating a blog entry:
if current_user.has_role?(:blog_writer)
current_user.blogs.create(params[:blog])
end
Adding a comment:
current_user.comments.create(:blog => blog, :content => "foor bar")
OR
blog.comments.create(:user => current_user, :content => "foor bar")

Rails: Many-to-many relationships with 3 Models

I have a situation where I have Products, Suppliers, ShoppingLists and Valuations.
A shopping_list consist of many valuations each with a product, an specific supplier and a price.
My models are as follows:
class Product < ActiveRecord::Base
has_many :valuations
has_many :shopping_lists, :through => :valuations
end
class Supplier < ActiveRecord::Base
has_many :valuations
has_many :shopping_lists, :through => :valuations
end
class ShoppingList < ActiveRecord::Base
has_many :valuations
has_many :products, :through => :valuations
has_many :suppliers, :through => :valuations
end
class Valuation < ActiveRecord::Base
belongs_to :product
belongs_to :supplier
belongs_to :shopping_list
end
My routes.rb is:
map.resources :shopping_lists do |shopping_list|
shopping_list.resources :valuations
end
map.resources :product
map.resources :supplier
I wonder if this could be the best solution, anyway what I want is that the user can create as many lists as he wish, each with several valuations.
The first time a shopping list is created its also filled with one valuation at least. Then, the user can add/remove valuations to/from the shopping_list.
I would like a simple and elegant solution, without Ajax callbacks.
What is the best way to do this, from the controllers/views/routes perspectives?
Or should I completley change my schema ?
Thanks!
Just found two excelent resources from Ryan Bates:
http://asciicasts.com/episodes/196-nested-model-form-part-1
http://asciicasts.com/episodes/197-nested-model-form-part-2
Let's see if that do the job!
// UPDATE: Worked great!

rails: self-referential association

My needs are very simple: I have a Tip table to receive comments and have comments to receive comments, too.
To retrieve each comment that is stored in the same table (comments), I created another key for the comments on comments: "inverse_comments".
I tried to use one comments table by using self-referntial association. Some resources seem to bring more than one table into the piture which are diffent from my needs. So I came up whth the following modeling for comments:
class Comment < ActiveRecord::Base
belongs_to :tip
belongs_to :user
has_many :mycomments,
:through => :inverse_comments,
:source => :comment
end
Apparently something is missing here but I cannot figure it out.
Could some one enlighten me on this:
what changes I need to do to make the model work?
thanks.
I believe you should use a polymorphic association.
For that you'll need to add a commentable_id and a commentable_type on your comments table. And your models should look like:
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :commentable, :polymorphic => true
has_many :comments, :as => :commentable
end
class Tip < ActiveRecord::Base
has_many :comments, :as => :commentable
end
This way you can use
#tip.comments
#comment.comments

Ruby on rails model with multiple parents

In my Rails application, I have two models, Articles and Projects, which are both associated with a user. I want to add comments to each of these models. What's the best way to structure this?
Here's my current setup:
class Comment < ActiveRecord::Base
belongs_to :article
belongs_to :project
end
class Article < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class User < ActiveRecord::Base
has_many :articles
has_many :projects
has_many :comments, :through => :articles
has_many :comments, :through => :projects
end
Is this the right way to handle the structure? If so, how do I manage the CommentsController to have an article_id if it was created through an Article, and a project_id if it was created through a Project? Are there special routes I should set up?
One final comment: Comments don't always have to have a user. Since this if for my website, I want anonymous viewers to be able to leave comments. Is this a trivial task to handle?
Make Comment a polymorphic model. Then create a polymorphic association.
Here's an example of polymorphic relationship from the Rails wiki and here's a Railscast from the screencast-men Ryan Bates.
You can check out - acts_as_commentable plugin http://github.com/jackdempsey/acts_as_commentable/tree/master
Or you can proceed with polymorphic relation
You could have ArticleComments and ProjectComments with similar structure but stored separately, then create a method that returns both types of comments.

Resources