This may seem like a real simple question, but do associations work both
ways?
I have a user model with, has_many :posts
I have a post model with, belongs_to :user
My posts controller has
@posts = Post.all
and my view does a '@posts.each do |post|' loop
In that loop, can I do:
<td><% post.user %></td>
...to give me back the user that made that post?
In the rails console I can do user.posts (that gives me all the posts a
user has made) but I can't do post.user (to give me the user).
I can do post.user_id which gives me the user_id, but I want their name
not their user_id - perhaps with something like post.user.name
Am I doing something wrong? I thought one of the benefits of
associations like this was so we wouldn't have to do separate queries?
Sorry if this is a silly question - I'm a Rails nube.
Thank you both for your help - I got it fixed with a little help from
Isaackearse on the Rails IRC channel.
The reason it was playing up was because I had some posts in the db that
weren't associated to any users - once they were deleted everything
works as expected
Is that only for db fields that are part of the 'join'/association?
Because I had some other fields that were empty too (but they belonged
to the post model) but they didn't cause any probs.
Or, you can push that into the model and avoid the extra logic in the view:
class Post
def user_name
self.user ? self.user.name : “(none)”
end
end
<% @posts.each do |post| %>
<%= post.user_name %>
<% end %>
You could name the method something like “author_string” or “by_line”, too. The idea is to keep the view very clean. You might be able to simply delegate :name, :to => :user in your Post model, but that would have given you the same problem if the post.user_id referred to a non-existent User.
-Rob
Search in app/model for the file User.rb (in singular)
This file exists??
For posterity that should be user.rb not User.rb of course
Or, you can push that into the model and avoid the extra logic in the
view:
class Post
def user_name
self.user ? self.user.name : "(none)"
end
end
<% @posts.each do |post| %>
<%= post.user_name %>
<% end %>
Why would you do that, though? Sure, it keeps a little bit of method
chaining out of the view, but at the cost of a completely unnecessary
model method.
If a calculation were being performed to get this value, I'd agree with
defining a method. But I think accessor method chaining in the view is
generally acceptable, particularly when it simply involves traversing
already loaded associations.
You could name the method something like "author_string" or "by_line",
too. The idea is to keep the view very clean.
Method chaining is much cleaner than this IMHO.
You might be able to
simply `delegate :name, :to => :user` in your Post model, but that
would have given you the same problem if the post.user_id referred to
a non-existent User.
Or, you can push that into the model and avoid the extra logic in the
view:
class Post
def user_name
self.user ? self.user.name : "(none)"
end
end
<% @posts.each do |post| %>
<%= post.user_name %>
<% end %>
Why would you do that, though? Sure, it keeps a little bit of method
chaining out of the view, but at the cost of a completely unnecessary
model method.
If a calculation were being performed to get this value, I'd agree with
defining a method. But I think accessor method chaining in the view is
generally acceptable, particularly when it simply involves traversing
already loaded associations.
But I will remind you that the OP found that the problems came from Posts that did not have an associated User. In such cases, post.user will be nil and nil.empty? does not exist. Perhaps you meant to say unless post.user.blank?, but if there are many places where a post.user_name is used, the extra method avoids Law of Demeter violations. (Or "Suggestion of Demeter" if you prefer.)
-Rob
You could name the method something like "author_string" or "by_line",
too. The idea is to keep the view very clean.
Method chaining is much cleaner than this IMHO.
You might be able to
simply `delegate :name, :to => :user` in your Post model, but that
would have given you the same problem if the post.user_id referred to
a non-existent User.
--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group.
To post to this group, send email to rubyonrails-talk@googlegroups.com.
To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
Or, you can push that into the model and avoid the extra logic in the
view:
class Post
def user_name
self.user ? self.user.name : "(none)"
end
end
<% @posts.each do |post| %>
<%= post.user_name %>
<% end %>
Why would you do that, though? Sure, it keeps a little bit of method
chaining out of the view, but at the cost of a completely unnecessary
model method.
The method DRYs up the code by not having to repeat
self.user ? self.user.name : "none"
wherever user name is to be displayed. Also it means the text to be
used when there is no user is defined in the model rather than being
scattered around the views.
chaining out of the view, but at the cost of a completely unnecessary
model method.
If a calculation were being performed to get this value, I'd agree
with
defining a method. But I think accessor method chaining in the view
is
generally acceptable, particularly when it simply involves traversing
already loaded associations.
But I will remind you that the OP found that the problems came from
Posts that did not have an associated User. In such cases, post.user
will be nil and nil.empty? does not exist.
Right you are. I committed the cardinal sin of not considering what you
wrote in light of the whole thread. Sorry.
Perhaps you meant to say
unless post.user.blank?, but if there are many places where a
post.user_name is used, the extra method avoids Law of Demeter
violations. (Or "Suggestion of Demeter" if you prefer.)
I'm aware of that. I don't think the Law of Demeter is particularly
helpful in Ruby.
Instead of creating a new method to delegate from the associated
object, I rather overload the association with a blank object if a
legitimate associated object is missing, which has the benefit of
allowing me to still use method chains in my views, but to also set
default values for missing objects:
alias_method :activerecord_user, :user
def user
activerecord_user || User.new(:name => "(none)")
end
There are a few gotchas... most of which I avoid by creating a new
"User", but not assigning it as an association (more object
instanciation, but if it was a real problem, I could assign it to an
instance variable...). As a balance between reducing the checks in the
view and creating (potentially) confusing methods, it does the job on
occasion.