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.