I have a problem with ordering search results on rails.
I want to order the posts after I searched or filtered but what I'm doing doesn't seem to work.
Here is my code.
posts_controller.rb
display
#posts = Post.where("user_id IN (?)", current_user.following_ids)
#posts = #posts.find(Like.group(:post_id).pluck(:post_id)).order(params[:change]) if params[:change].present?
end
I have a posts_controller which displays the posts user are following to, it works fine. However, when I added an extra code trying to order the posts by the number of likes, error occurs.
It seems that the second line in post controller extracts all the posts with like instead of only the posts the user are following. If I replace #posts.find with Post.find, all the posts result will be shown and ordered by the number of likes.
by the way, this, #posts.order(created_at: :desc), works fine. It orders only the posts user are following.
like.rb
class Like < ApplicationRecord
belongs_to :post, foreign_key: "post_id"
belongs_to :user
validates_uniqueness_of :post_id, scope: :user_id
end
like_controller.rb
class LikesController < ApplicationController
def create
#like = current_user.likes.create(post_id: params[:post_id])
redirect_back(fallback_location: root_path)
end
def destroy
#like = Like.find_by(post_id: params[:post_id], user_id: current_user.id)
#like.destroy
redirect_back(fallback_location: root_path)
end
end
I'm very new at programming so please forgive me if I'm asking a stupid question. Thank you all very much.
The error message is below:
Note that find, when given an array, requires that all of the id's in the array be present or you get an error. In the documentation, it states:
If one or more records can not be found for the requested ids, then RecordNotFound will be raised.
I would use a different mechanism to retrieve the records:
#posts = #posts.where(id: Like.group(:post_id).pluck(:post_id)).
order(params[:change]) if params[:change].present?
This will locate just those records that satisfy the condition.
This feels a little clunky. There might be a cleaner way of getting this result depending upon what relationship might exist in your models between Like, Post, and Group.
Related
I have four models - User, Agency, Listing and Inquiry.
A User has_one Agency, an Agency has_many Listings, and a Listing has_many Inquiries.
I have a query where I get a :user_id, so I can get its Agency and the collection of Listings.
I need the Inquiries that belong to a certain Listing.
My approach is the following, obviously I'm looking for a replacement for all. I'd like to make a query where I can list all the inquiries where :listing_id is the id of one of the listings in #listings.
def index
#agency = User.find(params[:user_id]).agency
#listings = #agency.listings
#inquiries = Inquiry
.all
render json: #inquiries
end
I tried combining various select, includes, where, etc. methods but couldn't come up with a working solution.
Thank you for your help, much appreciated!
We can use mutiple joins in a single query to reach till the resulted association.
def index
#inquiries = Inquiry.joins(listing: { agency: :user }).where(users: { id: params[:user_id] })
render json: #inquiries
end
First off, if you are using something like RuboCop it will probably warn you that you usually only pass one, possibly two instance variables in a method like this. Not that I haven't done it, it's just not considers optimal Rails. That said:
One way might be this:
def index
#agency = User.find(params[:user_id]).agency
#listings = #agency.listings
#inquiries = Inquiry.where(listing_id: #listings.pluck(:id))
render json: #inquiries
end
You can pass an array of things to match with where. pluck gives you an array of what ever columns you pass to it.
My app has simple user/post/vote structure, user can up-vote/down-vote on each post. I want to let user to see "their own unvoted posts" at the index page, I tried where where.not but can't combine it to work.
I tried to do this in the posts_controller and my best try is to find all unvoted posts but don't know how to get related to users.
def index
if logged_in?
#posts = Post.where.not(id: Vote.all.map(&:voteable_id))
else
#posts = Post.all.sort_by{|x| x.total_votes}.reverse
end
end
The vote is polymorphic association, below are the 3 models:
Post: has_many :votes, as: :voteable,
Vote: belongs_to :voteable, polymorphic: true,
User: has_many :votes
I have logged_in? to judge if #current_user exists.
Here is my repo: https://github.com/Tim-Feng/ad-judge
I am not sure what you mean by "their own unvoted posts" since the example code you have is kind of confusing.
If you need to find the current user's posts which have no votes you should do the following:
current_user.posts.where.not(id: Vote.pluck(:voteable_id).uniq)
If on the other hand you need to find the posts that have not been voted by the current user, you should be using only the current user's votes like so:
Post.where.not(id: current_user.votes.pluck(:voteable_id).uniq)
Hope it helps
TIP: if you need to make your code more efficient you can make it run all in one query by using left joins instead of searching for the ids to except.
This is a continuation of another question, but as it's different, I though I had better repost it as a new question:
Old Question
I'm adding quiz functionality to the twitter app from the Hartl tutorial and have these Models:
User is nearly the same as the tutorial:
class User < ActiveRecord::Base
has_many :followed_users, through: :relationships, source: :followed
has_many :takens, dependent: :destroy
has_many :questions, through: :takens
end
Taken is a table of Question ids to User ids:
class Taken < ActiveRecord::Base
belongs_to :user
belongs_to :question
end
nothing interesting in Question:
class Question < ActiveRecord::Base
attr_accessible :category, :correct, :option1, :option2, :option3, :qn
end
I want to be able to show followed_users and followers in order of the number of tests they have taken. In the console this can be had through:
User.find_by_id(1).question_ids.count
Then I can do something like:
User.find_by_id(1).followers.first.question_ids.count
in the console to get the count for a single follower.
I feel like I'm almost there.
How do I sort the followers and followed_users through their 'takens' count? (I was also looking at cache_count, which at first seemed promising, but might not be what I need...)
End Old Question
This is the answer from the other question: rails order through count on other table
and I went with a method like this in User.rb:
def users_sort_by_taken
User.find_by_sql("SELECT users.*
SELECT users.*
FROM users INNER JOIN takens
ON users.id = takens.user_id
GROUP BY users.id
ORDER BY count(takens.user_id) DESC")
end
which gets called in the users_controller.rb like so:
def following
require 'will_paginate/array'
#title = "Following"
#user = User.find(params[:id])
##users = #user.followed_users.paginate(page: params[:page])
#users = #user.users_sort_by_taken.paginate(page: params[:page])
render 'show_follow'
end
(For reference, the commented out line is from the Hartl tutorial)
all well and good, but now the current user is contained in the list of following (because of the above SQL). I need a way to eliminate the current user from the users_sort_by_taken.
I thought this might work:
WHERE (#{#current_user.id})
in the method,but I get this error:
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
I suppose I could pass it as an argument...
but don't I already have the user as #user in the following line?
#users = #user.users_sort_by_taken.paginate(page: params[:page])
Why can't I reference the current user from a method in the User.rb model?
Or another way, can I pass the current_user (or #user or user) to the SQL to exclude the current_user from the SQL results?
Any help appreciated.
Every object has its own set of instance variables - the fact that #user or #current_user is set in one object means nothing to another object.
The receiver of a method (in this case your user) is always available as self, so self.id gets you the user's id
The self is actually implicit - most of the time you won't need it and just writing id would result in the same thing (as long as you're in an instance method of that user)
To reference #user's id in the model, you can simply use:
self.id
I'm trying to create a multi criteria search form. I want to submit all of the pieces of the search via GET and if they have a value assigned I would like them evaluated. The thing that I'm having trouble grasping is building a query that will allow me to layer more queries on top when you're doing it with a through association.
Just to give you an idea of how my models are set up:
class Client < ActiveRecord::Base
has_many :campaigns
has_many :pieces, :through => :campaigns
end
class Campaign < ActiveRecord::Base
belongs_to :client
has_many :pieces
end
class Piece < ActiveRecord::Base
belongs_to :client
end
Now, with that model in mind, I'm using the collect method to grab the pieces that have an organization in common.
if params.has_key?(:criteria)
#selected_client = Client.where(:organization => "Org1")
#pieces = #selected_client.collect{ |c| c.pieces }.flatten
end
Is there some way of formatting that query string so that I can narrow #pieces down, a couple more times? Let's say I wanted to use that through association again, to get pieces that have another of the same Client criteria...
Thanks a ton! My brain is a pretzel at this point.
I'm not sure if i undestand very well what you're trying to do. If you want to get all pieces matching your client criteria, in rails 3, you can do this :
#pieces = Piece.joins(:campaign => :client).where(:clients => {:organization => criteria})
to get all pieces belonging to clients from organization "Org1".
You can stack as many where statements as you want to add new conditions, as #Andrew said. See this for more information.
Your first paragraph got cut off, just FYI.
If #pieces is an array, you can use a find block to narrow your search further. Although this will put the load on your server CPU rather than the SQL database.
You can stack where statements and Rails will automatically create a single query for you. Here is some sample code from the app store section of our website:
#platform = params[:platform]
#category = params[:category]
#search = params[:app_q]
# Get initial SQL, narrow the search, and finally paginate
#apps = App.live
#apps = #apps.where("platform = ?", AppPlatforms.value(#platform)) if AppPlatforms.value(#platform)
#apps = #apps.where("(name LIKE ? OR description LIKE ?)", "%#{#search}%", "%#{#search}%") if #search
#apps = #apps.where("id IN(?)", #cat.apps) if #category && #cat = Category.find_by_id(#category)
#apps = #apps.paginate(:page => params[:page], :per_page => 10, :order => "name")
Instead of using collect you should be able to use #selected_client.pieces.where(...) to narrow your search down via SQL.
I hope this is a point in the right direction!
I have a model posts, which belongs_to category (which uses friendly_id). Now i want to list all Posts in an Category. To get the index page i want to use a link like: http://mysite/posts/category/_category_slug_, for that i made the following route:
match 'posts/category/:category/' => 'posts#index'
And in my post controller i got:
def index
if params[:category]
#posts = Post.all(:joins => :category, :conditions => {"categories.cached_slug" => params[:category]})
else
#posts = Post.all.reverse
end
...
It works like it should, but i dont think its the friedndly_id way to do it.
Is there a better way to achive this? thanks for your help.
FriendlyId adds the ability to do a find on a model using the slug, and is smart enough to check the cached_slug column first.
You can achieve the same result by performing a find on the Category model first then getting all the posts.
This assumes there is a has_many and belongs_to association in place with the referencing ID columns (or HABTM)
def index
if params[:category]
#posts = Category.find(params[:category]).posts
else
#posts = Post.all.reverse
end
...
Since you're passing in a category param (being friendly_id), it makes sense to reference it via the Category model.
-- more info added --
ALSO: Historical finds will still work ..
So, if you have generated a new slug by renaming a category, the old url will behave correctly (great if you're avoiding 404's)