Can a model belong_to more than one model? - ruby-on-rails

class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
So with the above association can I fetch both user and post details from a given comment object?.
like
#comment.post.post_title and
#comment.user.user_name.
Also please note that I have used comment as a nested resource of post.
resources :posts do
resources :comments
end

Yes you can, and you don't need to specify the foreign key or class name to do so. Saying belongs_to :user means rails will look for a user_id integer field in the comments table, and expect an ActiveRecord class named User to exist.
Add as many as you like, they don't interfere with each other.

Related

How to get information from child table

I have a Users model with has_many :comments
Each comment records the current_user to the commenter column.
How can I call #commenter.username in my view and show the commenter's username?
Currently in comments_controller using: #commenter = User.find("2") will pull the correct information. I would like to use something along the lines of: #commenter = User.find_by_id(:commenter)
From the comments, it sounds like you have this Comment model:
class Comment < ActiveRecord::Base
belongs_to :user
end
The comment model represents comments on a user's page, where the user model is defined as:
class User < ActiveRecord::Base
has_many :comments
end
There is also a commenter column on the comments table to store the user who posted the comment (on the other user's page).
First, I would recommend using commenter_id for this column rather than commenter, since this is the convention for storing ids of any kind.
Assuming that you have a commenter_id column on the comments table, you would then define a second relationship as follows:
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :commenter, class_name: "User", foreign_key: :commenter_id
end
The commenter relationship defines the user who wrote the comment, rather than the user whose page the comment is on.
For the other side of this relationship, update your User model:
class User < ActiveRecord::Base
has_many :comments
has_many :page_comments, class_name: "Comment", foreign_key: :commenter_id
end
Here, page_comments defines the comments this user has posted on other users' pages.
Then if you have a #comment, you can get the commenter's name with simply:
#comment.commenter.name
As long as you setup your column names and models correctly, dealing with getting information like this should be very simple.

Reason for belongs_to and has_many associations in ruby-on-rails

I don't get why need both these associations?
Surely if you define one, it would naturally imply the other. Therefore making one of them redundant.
Is there ever a situation where you would have one and not the other?
For example if I define:
class Post < ActiveRecord::Base
has_many :comments
end
why do I then need to define:
class Comment < ActiveRecord::Base
belongs_to :post
end
Surely the above could be implied, and therefore use convention over configuration.
I'm just curious what the thinking was behind this! I'm sure there must be a good reason.
It's not that straight forward. For example if you have a belongs_to relation, the other end could represent a has_one or a has_many.
Secondly, sometimes you don't want the inverse association as it clogs up your model on the other end of the association. For example, you have a lot of classes referencing user but you don't need to know that a user has_many backorder_refirbished_car_parts
First for this code it should be belongs_to if i understand right, as has_one will search at Post for comment_id and i don't think its the case for you and comment who has post_id defined on it
class Comment < ActiveRecord::Base
# has_one :post
belongs_to :post
end
Second: if you don't want to define it, then You can do it but you will not be able to say:
Comment.first.post
so if you don't need it don't write it.
Its not like needed. If you want only belongs_to or has_many association you could. There is not a dependency, If you followed convention of foreign key.

Various belongs_to many associations

I've found good answers here, here, and here but I'm having trouble generalizing that to what I'm after.
I have multiple categories, that will be curated and selectable. So, users will be able to select cat1, cat2, and cat3, but not type a custom category.
A category can have many posts, a post can have many categories.
A post can have many comments.
A user can have many posts, and many comments.
For the post/category relationship, I'm thinking this will work, but the user/post/comment relationship is where I'm scratching my head...
# app/models/category.rb
class Category < ActiveRecord::Base
has_and_belongs_to_many :posts
end
# app/models/post.rb
class Post < ActiveRecord::Base
has_and_belongs_to_many :categories
belongs_to :user
has_many :comments
end
# app/models/user.rb
class User < ActiveRecord::Base
has_many :posts
has_many :comments
end
# app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
Does this look close? Do I need any foreign keys anywhere to handle all this? Thanks in advance, I'm sure this is simple and I'm missing something obvious in my understanding.
And then I have to worry about how to write the tests for all this! That's for another day though...
EDIT: I should point out, I haven't started this yet. Just trying to map it out before I start, so it should simplify things, fewer migrations, etc.
EDIT AGAIN: Implemented suggested changes so far. Thanks!
why not start with the specs first? is a good practice on rails with all the power you have with rspec
Your Item should be called Post, why Item? is there any reason? if you want to call it "Item" you need to specify that on the associations
belongs_to :post, class_name: 'Item'
but you are better with Post instead of Item
A comment belongs to a user so the the user has_many :comments, you don't need the ", through: :posts" part
has_many :category_posts
has_many :posts, :through => :category_posts #or would has_and_belongs_to_many work better?
this depends on you, you need extra behavior on the CategoriesPosts? (Categories, in plural) if not, just use has_and_belongs_to_many
Really, i would suggest you start with the specs, you will end up with the implementations without thinking it too much and then you already have it tested and then you can add more specs and refactor it. Read something about TDD and BDD, it's hard at first but it's really good when you get it.
The only change I think I would make to this, other than actually naming Item Post, would be on your user model:
# app/models/user.rb
class User < ActiveRecord::Base
has_many :posts
has_many :comments
end
You don't need a through association there. You could add other scoped comments to be something like comments_on_my_posts, through: :posts, class_name: "Comment", but for the above association on comments, it should be direct (commenter <=> comment).

Associations in ruby on rails

I have created a user using device authentication. I also have created article view, controller and model where model has fields such as title, body and article_id. Now I want to implement comments (with the condition that only logged in user can comment on an article). I have created an is_admin as a special user that has power create new articles through application.
The User table has all fields that are default generated by device. The Article table has fields like article_id, title, and body. There is still no association between user and article table. The Comment table will have (according to my understanding) comment_id, and comment_body.
Expected Associations:
class Comment < ActiveRecord::Base
belongs_to :article
belongs_to :user
I want to make sure that only logged in users can comment on articles, and that is_admin user can create new articles.
How can I create association between user, article and comment tables? Do I need to create association for user and comment table?
It is recommended to go through the rails guide and small blog to understand association as #dpassage suggested in comment. Looks you already have work around, so let me consolidate.
As you described, you will have three model:
First: User
class User
has_many :articles # not dependent => :destroy, may you not want to destroy article on deletion of user
has_many :comments, :dependent => :destroy
end
Second: Article
class Article
has_many :comments, :dependent => :destroy
belongs_to :user
end
Third: Comment
class Comment
belongs_to :user
belongs_to :article
end

how to model a many to many relationship

Here is the scenario,
Articles have many Comments
Users can write many Comments for many Articles
The comments table contains both
user_id
article_id
as foreign keys
My models are set up like so
class User < ActiveRecord::Base
has_many :comments
has_many :articles, :through => :comments
class Article < ActiveRecord::Base
has_many :comments
has_many :users, :through => :comments
class Comment < ActiveRecord::Base
belongs_to :users
belongs_to :articles
My routes.rb has the following code
map.resources :articles, :has_many => :comments
map.resources :users, :has_many => :comments
which produces the following routes
new_article_comment
edit_article_comment
new_user_comment
edit_user_comment
etc...
This is not what I want (atleast not what I think I want), since comments must always be related to users and article, how can I get a route like so
new_user_article_comment
edit_user_article_comment
Then I could just do
new_user_article_comment_path([#user, #article])
to create a new comment
You could probably do this using nested routes. This article has a good example of how to do that:
http://weblog.jamisbuck.org/2007/2/5/nesting-resources
Note that the article is basically saying "don't do it":
"Rule of thumb: resources should never
be nested more than 1 level deep. A
collection may need to be scoped by
its parent, but a specific member can
always be accessed directly by an id,
and shouldn’t need scoping (unless the
id is not unique, for some reason)."
However, the above quote doesn't really apply to your action, as well as other cases where the unique id is not (yet) available, since you don't yet have a unique id for the comment, and the user and article ids are absolutely necessary. A comment on that same page offers this qualification:
Thijs van der Vossen said...
"We’ve invented a second rule of thumb;
resources should only be nested for
actions that really need the parent id
(like ‘index’, ‘new’, and ‘create’).
We started using this for an
application with a data model that’s
almost entirely hierarchical except
where it’s not and it seems to work
very nicely. Why keep the parent
resource in the url when the structure
is not strictly hierarchical?"
You have created a circular database schema. Not really recommended.
The data model should look like this:
class User < ActiveRecord::Base
has_many :comments
class Article < ActiveRecord::Base
has_many :comments
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :article
And the basic routing:
map.resources :articles do |articles|
articles.resources :comments
end
And then decide what else do you need to make it fit your requirements.

Resources