简体   繁体   中英

rails map.resources with has_many :through doesn't work?

I've got three (relevant) models, specified like this:

class User < ActiveRecord::Base
  has_many :posts
  has_many :comments
  has_many :comments_received, :through => :posts, :source => :comments
end

class Post < ActiveRecord::Base
  belongs_to :user
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :user
  belongs_to :post
end

I'd like to be able to reference all the comments_received for a user with a route - let's say it's for batch approval of comments on all posts. (note that you can also get comments made by the user , but users can't comment on their own posts, so the comments through a post are different and mutually exclusive) . Logically, this should work with:

map.resources :users, :has_many => [:posts, :comments, :comments_received]

This should give me routes of

user_posts_path
user_comments_path
user_comments_received_path

The first two work, the last one doesn't. I've tried it without the _ in comments_received to no avail. I'm looking to get a URL like

http://some_domain.com/users/123/comments_received

I've also tried nesting it, but maybe I'm doing that wrong. In that case, I'd think the map would be:

map.resources :users do |user|
  user.resources :comments
  user.resources :posts, :has_many => :comments
end

and then the url might be:

http://some_domain.com/users/123/posts/comments

Maybe this is the right way to do it, but I have the syntax wrong?

Am I thinking about this the wrong way? It seems reasonable to me that I should be able to get a page of all the comments added to all of a user's posts.

Thanks for your help!

Although the syntax to deinfe resource and model relationship is similar, you shouldn't be fooled into thinking that a resource maps to a model. Read what David Black has to say .

The problem you're having is with the routes you're generating. Using the nested syntax like so:

map.resources :users do |user|
  user.resources :posts
  user.resources :comments
  user.resources :comments_received
end

And then running 'rake routes' , gives me (amongst loads of other stuff!):

                       users GET /users                              {:action=>"index", :controller=>"users"}
                  user_posts GET /users/:user_id/posts               {:action=>"index", :controller=>"posts"}
               user_comments GET /users/:user_id/comments            {:action=>"index", :controller=>"comments"}
user_comments_received_index GET /users/:user_id/comments_received   {:action=>"index", :controller=>"comments_received"}

So it appears that rails is adding _index to the end of the comments_received route. I'll admit I don't know why (something to do with clashing with the other comments route?) but it explains your problem.

An nicer alternative might be to define a collection action on your comments resource, like so:

map.resources :users do |user|
  user.resources :posts
  user.resources :comments, :collection => {:received => :get}
end

This will give you the following routes:

                 users GET /users                             {:action=>"index", :controller=>"users"}
            user_posts GET /users/:user_id/posts              {:action=>"index", :controller=>"posts"}
         user_comments GET /users/:user_id/comments           {:action=>"index", :controller=>"comments"} 
received_user_comments GET /users/:user_id/comments/received  {:action=>"received", :controller=>"comments"}

Note: the received action is now on the comments controller

I have to confess, I'm a little confused by the variable names, but first off, I'm surprised your has_many :through works at all the way it's defined. Do the models behave as you expect, setting aside the routes for a second?

Second, and this is where the variable names really come into play, the routes have some dependencies on pluralization, so your foos bars and bazs might either be a cause of the problem, or might be hiding the problem. In any event, you can definitely write something like this:

map.resources :users do |user|
  user.resources :awards
  user.resources :contest_entries do |contest_entry|
    contest_entry.resources :awards
  end
end

which I believe would give you:

user_path, user_awards_path, user_contest_entry_path, and user_contest_entry_awards_path.

I'm not sure if this really answers your question, and it might help to get a clearer picture of what's going on here if you changed foo, bar, and baz to something closer to the real situation.

A quick-n-dirty solution would be to add a custom method (eg getusercomments) to your users-controller that would return all the comments:

def getusercomments
@user = User.find(params[:id])
@comments = @user.posts.comments
end

Then add this method to your users-route:

map.resources :users, :member => { :getusercomments => :get }

Afterwards you should be able to call the following to get all comments of a user:

http://some_domain.com/users/123/getusercomments

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM