views:

201

answers:

2

Hi All,

I have 3 model, User, Post, Comment with definition like below

    class Post < ActiveRecord::Base
      belongs_to :user
      has_many :comments
      def self.find_other_user_posts
           ?
      end
    end
    class User < ActiveRecord::Base
      has_many :posts
      has_many :comments
      has_many :posts_commented_on, :through => :comments, :source => :posts
    end
    class Comment< ActiveRecord::Base
      belongs_to :post
      belongs_to :user
    end

User can have many post and many comment, question is I want to make method in Post model that returning collection of posts. The method will find posts and comments that doesn't belong to current user. For example there are

Post: A have Comment: A1
Post: B have Comment: B1, B2, B3, B4
Post: C have Comment: C1, C2
Post: D have Comment: nil
Post: E have Comment: nil
Post: F have Comment: F1, F2, F3

current user own Post A, E and Comment A1, B2, so calling :

@posts = Post.find_other_user_posts

will return collection of C, D, F posts.

Thanks

@Terry, following your solution I manage to add

@posts = Post.find( :all,
    :include => [:comments],
    :conditions => ["posts.user_id <> ? and (comments.user_id <> ? or comments.user_id is null)", current_user, current_user])

And for example data like below Pn for post Cn for comment Un for user and I'm showing the sample data in join form

Post,Post Owner,Comment, Comment Owner
P1,U1,C1,U2
P1,U1,C2,U2
P1,U1,C3,U2
P1,U1,C4,U3 *
P1,U1,C5,U4 *
P1,U1,C6,U5 *
P2,U1
P3,U2
P4,U3,C7,U2
P4,U3,C8,U2
P5,U4

P2, P3, P5 doesn't have a comment and assume current_user is U2, the Post I want to collect is P2 and P5 because U2 is making a comment on P1 and P4 and U2 is the owner of P3 so what left is P2 and P5.

But with above code It return P1, P2, and P5, correct for P2 and P5 but what about P1 ?

Why It's return P1 ? and when I'm drawn it step by step I realize that the row with star (*) is the problem one because these three row is TRUE for the three conditions that I apply in code... and so I'm confuse right now, are there anyway to fix the code with just 1 sql call ?

+5  A: 
@posts = Post.find(:all,
           :include => [:comments],
           :conditions => ["posts.user_id <> ? and comments.user_id <> ?",
                           user.id,
                           user.id])

Note that "user" here is not the associated user of the Post model. Look into using named_scopes to implement this behavior.

Terry Lorber
thanks, but it doesn't include Post D with nil/zero comment, how to include post with nil comment ?
gkrdvl
The :include generates a LEFT OUTER JOIN, so posts without comments should be returned.
Terry Lorber
Terry, I follow exactly the code above but it resulting in a strange collection (like post with nil comment), it is because of my Rails 2.3.2 ? Yes I check the log and I see that include generate a LEFT OUTER JOIN and it seems the sql is doing the job right, but combining Terry and Ez solution I manage to do something like @posts = Post.user_id_does_not_equal(current_user) - current_user.posts_commented_on.all, it solve the problem but did it have an issue like big collection ?
gkrdvl
gkrdvl, Terry's solution should work, what is it 'strange'? if there are nil comments try to add "and comments.id is not null" in the conditions part.
ez
I try to minimize by erasing post condition and change comment condition to equals@posts = Post.find(:all,:include => [:comments],:conditions => ["comments.user_id = ?", user.id])It works GREAT but when I change equals to does not equal :@posts = Post.find(:all,:include => [:comments],:conditions => ["comments.user_id <> ?", user.id])It result in a strange collection, I try a lot of sample data and can't find the result pattern because it's very weird....
gkrdvl
@gkrdvl Posts with no comments should be returned, per your question. Maybe "<>" is not the proper syntax for your database. Try "!="?
Terry Lorber
@Terry and @ez, please check the "addon" on the question about my problem :D
gkrdvl
@Terry "<>" and "!=" is the right syntax, both work with same result :D
gkrdvl
+2  A: 

i want to try a different approach:

 @posts = Post.find_all - current_user.posts - current_user.comments.map(&:posts)
ez
magic, it works, thanks, but are there anyway to move this approach to Post model ?
gkrdvl
yes, you can. you can create a method and pass in the user - def find_all_posts_except_for_user(user), and replace the current_user to user
ez
This results in three separate DB queries, it would be more efficient to use the RDBMS to generate the desired result set. Not to mention that Post.find_all could be a very big collection.
Terry Lorber
Terry, yes you are right. Depending on the numbers of records, it may or may not be appropriate. Push to the SQL is generally a better approach.
ez
Terry, you pinpoint a big issue here, thanks but I manage to minimized it with @posts = Post.user_id_does_not_equal(current_user) - current_user.posts_commented_on.all (using searchlogic), but did it have a big collection issue ?
gkrdvl