Do associations work both ways?

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 :confused:

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.

Thanks in advance!

As long as you have that belongs_to, post.user should work. What do
you mean when you say that you can't?

Fred

Hi Fred, thanks for the reply.

In the rails console with 'post.user' I get:

NoMethodError: undefined method `user' for #<Post:0x00000101455590>

And in my view 'post.user.nameI get:

undefined method `name' for nil:NilClass

If I leave off '.name' I get what looks like an object:

<User:0x000001031f4470>

It’s not normal

try reboot your server

check in DB if user_id is null

try Post.first.user.name

Hi Rodrigo, thanks for the reply.

I've tried that - but still doesn't work :frowning:

I pushed the app to git:

https://github.com/Brook/uf/blob/master/app/views/topics/index.html.erb

And you can see where I'm trying to call it on line 18

Maybe you can spot where I've gone wrong? :confused:

Search in app/model for the file User.rb (in singular)

This file exists??

Paste the code of this file here.

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 :slight_smile:

Thanks again to everyone for their help :smiley:

For posterity that should be user.rb not User.rb of course

Colin

Great!!

when user_id is null (and can be null in some cases)

you can do this

<%post.each do |p|%>

<%=p.user.name unless p.user.nil?%>

<%end>

other way is

<%=p.user.name unless p.user.empty?%>

Thanks Rodrigo.

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.

No, no, if field is null or empty you can use this.

It’s not specifc for join or association

You are learning the ROR, and is better for you use .empty?

It’s will cause less errors for you

Sorry for my english… i’m still learning =D

Thanks again Rodrigo, and no worries about your English... it is very
good!

If you're on twitter, please feel free to add me :slight_smile: my username is:
AstonJ

Great!!

when user_id is null (and can be null in some cases)

you can do this

<%post.each do |p|%>

<%=p.user.name unless p.user.nil?%>

<%end>

other way is

<%=p.user.name unless p.user.empty?%>

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

Colin


Rodrigo Mendonça
(62) 8567-3142

Rob Biedenharn

Rob@AgileConsultingLLC.com http://AgileConsultingLLC.com/

rab@GaslightSoftware.com http://GaslightSoftware.com/

Rob Biedenharn wrote in post #968091:

other way is

<%=p.user.name unless p.user.empty?%>

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.

delegate seems smelly in this context.

-Rob

Best,

Thanks for the replies Rob and Marnen - they are both very helpful to me
as a nube :slight_smile:

Rob Biedenharn wrote in post #968091:

other way is

<%=p.user.name unless p.user.empty?%>

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.

delegate seems smelly in this context.

-Rob

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org

--
Posted via http://www.ruby-forum.com/.

--
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.

Rob Biedenharn
Rob@AgileConsultingLLC.com http://AgileConsultingLLC.com/
rab@GaslightSoftware.com http://GaslightSoftware.com/

Rob Biedenharn wrote in post #968091:

other way is

<%=p.user.name unless p.user.empty?%>

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.

Colin

Colin

Rob Biedenharn wrote in post #968133:

class Post

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.

-Rob

Best,

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.