I have created a section for my latest posts, and a section for all posts. However, my last created post gets shown twice.
In my controller, how do I show all posts except for the last post?
MainController
def index
#post = Post.all.order('created_at DESC')
#latest_post = Post.ordered.first
end
You're querying twice. Instead, query once, and pull the latest post off the result set:
def index
#posts = Post.all.order('created_at DESC').to_a
#latest_post = #posts.pop
end
I'm not completely sure which side of the results you're considering the "first" record, so if #posts.pop appears to give you what you consider the "last" record then use #posts.shift to remove the record from the opposite end.
This will not fetch #latest_post in #post
def index
#latest_post = Post.ordered.first
#post = Post.where.not(id: #latest_post.id).order('created_at DESC')
end
Or simply
def index
#latest_post = Post.last
#posts = Post.where.not(id: #latest_post.id).order('created_at DESC')
end
Related
I've created the sample rails app with a list of apps showing in a DataTable. If I select one of the rows, I want the view of the post to then show a table with the list of comments for that post below it. In the post controller, I have:
# GET /posts
# GET /posts.json
def index
#posts = Post.all
end
# GET /posts/1
# GET /posts/1.json
def show
#post = Post.find(params[:id])
#comments = Comment.find(params[:id])
end
The result of the above it to give an error that it can't find a comment with id = 1.
What I'm trying to do is end up with a list just like the index returns for the posts that I can use on the post.html.erb page to create the list. What arguments do I use to collect the comments for the found post ( if they exist )? The comments schema has a column named 'post_id'.
You are using the same id you receive in your action to retrieve both the post and its comments. This is wrong, that's just the post id. You can retrieve them using your post like this
def show
#post = Post.find(params[:id])
#comments = Comment.where(post_id: #post.id)
end
or, better, if you have a comments association defined in your Post model
def show
#post = Post.find(params[:id])
#comments = #post.comments
end
I am getting the error message "wrong number of arguments error(1 for 0) in my posts controller in my show action. I will comment the end of that specific line. Thanks for the help.
def show
#post = Post.all(:order => 'created_at DESC') #this is the error line
end
def new
#post = Post.new
end
def create
#post = Post.new(params[:post])
if #post.save
redirect_to #post
else
render :new
end
end
You'll want to read the the latest Rails Active Record Query Interface Guide. A lot has changed lately. The short answer is that you now chain together conditions. And .all does not take any arguments -- as the error message is telling you. Instead, you want to use the .order() method:
#posts = Post.order(created_at: :desc)
You either have to show 1 post, and it will be:
#post = Post.find(params[:id]) # show
or all posts
#posts = Post.order('created_at DESC') # index
Taking into account the fact, you write this in show action, you probably meant first.
Also small recommendation regarding strong parameters.
Instead of writing this #post = Post.new(params[:post]) you would rather want to write in #create:
#post = Post.new(post_params)
private
def post_params
params.require(:post).permit(:title, :body) #whatsoever your post has
end
I'm trying to sort out how to loop through two object arrays and display them in mixed order in a view – by the date they were created.
Here's my current controller code, that only displays Posts based on the category you're in, or, based on a search query:
def browse
#user = current_user
if params[:category]
#category = Category.find(params[:category])
#posts = #category.posts.page(params[:page])
else
#posts = Post.search(params)
end
end
In the view, I just loop through and output these like so:
- if #posts
- #posts.each do |post|
= post.name
= post.content
What I'd like to do, is instead of referencing posts via the instance variable #posts... I'd like to create a new variable (ie: #everything) – that pulls objects from the Post class and the Comment class, pops them into the same array, and allows me to loop through and output each respectively in my view, like this:
Ideal controller:
def browse
#user = current_user
if params[:category]
#category = Category.find(params[:category])
#everything = #category.everything(params[:page]) # ... combination of comments and posts
else
#everything = Everything.search(params)
end
end
Ideal view:
- if #everything
- #everything.each do |e|
- if e.type == 'post'
= e.name
= e.content
- else
= e.comment
Any help/guidance is appreciated. I'm just not sure how to approach this.
You would do this type of thing (to get you started)
def browse
#user = current_user
#everything = #category.posts | #category.comments
end
In the view
%ul= render #everything
Make sure there is a views/comments/_comment.html.haml and a views/posts/_post.html.haml files.
Or you could render a specific partial and handle any differences in there
%ul= render :partial => shared/everything_item, :collection => #everthing
I wanted to know how one would do the following:
A user can view all published Posts
A user can view view their unpublished Post
Code:
# Post model
scope :published, where(is_published: true)
scope :unpublished, where(is_published: false)
# Post controller
def index
#Post = Post.published
if user_signed_in?
#Post = Post.published && Post.unpublished.where(user_id: current_user.id)
end
end
I'm not really sure what the right way to setup an active record condition to display what I'm after.
Any much is much appreciated.
You're pretty close! Just replace && with +
# Post controller
def index
#posts = Post.published
if user_signed_in?
#posts = Post.published + Post.unpublished.where(user_id: current_user.id)
end
end
Be aware that joining like this will change the #posts object from a relation to an array.
Also take a look at #SachinR's answer for a nice refinement to the Post.unpublished.where(user_id: current_user.id) line.
Based on your requirement I think you could do better with a scope:
#Post model
scope :published_and_user, lambda{|user| where("is_published = ? OR user_id = ?", true, user.id)}
scope :ordered, :order => "created_at DESC"
# Post controller
def index
#posts = Post.published.ordered
if user_signed_in?
#posts = Post.published_and_user(current_user).ordered
end
end
And now you have a relation that is ordered properly, and only one scope!
To get all published records
#posts = Post.where("user_id = ?", current_user.id).published
To get all unpublished records
#posts = Post.where("user_id = ?", current_user.id).unpublished
or
If Post belongs to user
class Post
belongs_to :user
end
then you can directly use
current_user.posts.published
current_user.posts.unpublished
I'm working on a blog like application,
my user module has_many posts and the posts module belongs_to user
I want to access both users/:id/posts and posts/
routes.rb is something like this:
resources :users do
resources :posts
end
resources:posts
how can i know within the posts controller if its accessed directly (/posts) or through the nested route (/users/:id/posts) ?
for example, what should be the index method of the posts controller for doing the correct INDEX action for /users/:id/posts and for /posts
is there a better way for doing this ?
One solution could be to use a before filter on your controller, like:
before_filter :load_user
def load_user
#user = User.find(params[:user_id]) if params[:user_id]
#posts = #user ? #user.posts : Post.all
end
Then you have to rewrite your controller a bit to function properly.
No refactoring needed on index action, #posts already loaded correctly, but you can do further filtering as you like
def index
#posts = #posts.where('updated_at < ?' Time.now)
end
Then update every member action: new, create, show, edit, update, destroy and use posts as a base like:
def new
#post = #posts.build
end
def create
#post = #posts.build(params[:task])
end
def show
#post = #posts.find(params[:id])
end
def edit
#post = #posts.find(params[:id])
end
def update
#post = #posts.find(params[:id])
end
def destroy
#post = #posts.find(params[:id])
end
Of course you can add other before filters to remove duplicate code.
Check the params.
If just post you'll just have :id
If user/post you'll have user and ID for post.
So check if params[:user]...
n.b. If not user, try params[:user_id]
As for the index method for posts I think it will actually be the SAME in both cases. What will change things is its usage, association and scoping within user.