I have a User and Post scaffolded in my rails application. I wanted to get all of the posts associated with a user. For example, I want to be able to do something like this:
localhost:3000/users/1/posts
And it should give me all of the posts with the user_id = 1. I know how to get all the posts via the rails console for a specific user. I was just wondering how i can accomplish something like the example above. Would I have to add another method in the users controller?
You can do it without adding new action. Like this:
class PostsController < ApplicationController
def index
#user = User.find(params[:user_id])
#posts = #user.posts
end
end
another one line example is the following, add this to your index
#posts = User.find(params[:user_id]).posts
I wrote a longish answer to a related question that may be helpful:
https://stackoverflow.com/a/17911920/321583
Related
I need to display post with the latest made comment in it, on top of page. And every Post has many comments and many Comments belong to one post.
Here is my index method from Posts controller
def index
#posts = Post.all.order("posts.created_at desc")
def new
#post = Post.new
end
end
I looked through Rails docs and found .order and .where methods, and I think those two methods are the solution to my problem but I am not sure how to use it
try this:
#posts = Post.joins(:comments).order("comments.created_at DESC")
def index
#posts = Post.all.order("created_at DESC")
#latest_post = #posts.first
end
update
I interpreted word comments as posts.
update 2
First you need to find the latest comment:
#latest_comment = Comment.all.order('created_at DESC').first
Having that, you can extract the ID of the Post to which this comment belong:
post_id = latest_comment.post_id
Now you have the latest comment and the respective post's id. I would modify index page like this:
def index
#posts = Post.all.order("created_at DESC")
#latest_comment = Comment.all.order('created_at DESC').first
#post_of_latest_comment = Post.find(#latest_comment.post_id)
end
I am not sure what do you mean by displaying it on top, but I am pretty sure with this code you can do it in your view.
update 3
In your view, you hsould have something like this:
<h1>Top comment</h1>
<%= #latest_comment.text %>
By .textI mean some attribute of model Comment wich contains the content of the comment, plain text. If you need more help with this, show what atributes your Post and Coment model have.
I tried a modified line from Pitabas Prathal:
Post.joins(:comments).order("comments.created_at DESC").group('post_id')
where post_id is the foreign key in the Comment model from the Post model. It worked fine for me :)
Task: Showing the profile of an employee straight away after his login.
Issue:
class WelcomeController < ApplicationController
def index
#employee = Employee.find_by_email(params[#current_user.email])
end
end
I tried to code in many ways to associate the email of the current user with his respective details from the employees table and the farthest that I could get was it:
I am sure that I am writing something wrong in this line inside the index thing, but I am researching and all things that I found and tried did not get the employee related to the current user.
Try with this code:
class WelcomeController < ApplicationController
def index
#employee = Employee.where(email: current_user.email).first
end
end
When using Devise, the current user is an instance variable, so you don't need to prefix it with #.
If you are going to have a lot of users, is a good practice to create an index in your database for the email column.
I like kjmagic13's answer. Use the
#current_user.id
It pulls all the info associated with the user from the database
Mori's answer is also good.
The following SQL line in your logs corresponds to the Employee.find_by_email call:
SELECT "employees".* FROM "employees" WHERE "employees"."email" IS NULL LIMIT 1
As Mori pointed out, this means you're finding the employee with a nil email, which means that params[#current_user.email] is nil. Since you have no parameters, there's no need to refer to the params hash regardless. You should refer just to the #current_user.email:
Employee.find_by_email #current_user.email
As Mori's answer states, you probably didn't intent do use #current_user.email as a hash key into params. I think you're trying to look up the employee record for the current user by email (not by an email submitted as a parameter), like so (also avoiding deprecated find_by_* helpers):
#employee = Employee.find_by(email: #current_user.email)
I don't think you want to try to do Employee.find(#current_user.id) - that's just going to look up the Employee whose id matches the current_user's id - unless Employee and User use the same table that's not going to be meaningful
Why not just find by the ID? find_by_* are old.
#employee = Employee.find(#current_user.id)
I have a store application with a Product scaffold and I want to enable categories and pages that show each category of products.
My product model has a "category" attribute and I use the link_to helper to create links to each category.
In my products controller I added a method called index_by_category(cat):
def index_by_category(cat)
#products_by_category = Product.where(category: cat)
end
I'm trying to iterate #products_by_category in a view I created with the corresponding name (product/index_by_category.html.erb) just like the regular index method do. For some reason it render me the regular index method of products which shows ALL of them, even though the URL is:
http://localhost:3000/products?index_by_category=Food
This is what I did in my route.rb file:
get 'products/index_by_category'
I'm newbie to Rails development so if I did something which is wrong from the roots and the rails approach to the problem should be entirely different I also be happy to know for the sake of learning.
You are doing things a bit wrong. Try to write your controller like this:
def index_by_category
#products_by_category = Product.where(category: params[:category])
end
And update your route
get 'products/category/:category', to: 'products#index_by_category
Then visit
http://localhost:3000/products/category/Food
UPDATE
if you really want to use index method for both cases you could do that by modifying it to something like this
def index
if params[:category]
#products = Product.where(category: params[:category])
else
#products = Product.all
end
end
and then just visit
http://localhost:3000/products?category=Food
I'm wondering if it's possible to edit the default Rails routing convention to fetch a specific record based on a field that is not the ID?
For instance, instead of retrieving a specific record based on ID, with the verb/url combination:
GET /users/:id
Retrieve a specific record based on username, with the verb/url combination:
GET /users/:username
I don't see why this would be a problem theoretically, as long as usernames were required to be unique, but I'm having trouble understanding how to implement it based on the Rails Routing Guide.
I have gathered that I will need to add a line to my routes.rb file, to define a singular resource, just prior to:
resources :users
However, I'm having trouble understanding the syntax to accomplish this. Any help in understanding this would be greatly appreciated.
Yes it is possible and they are called Non Restful Routes in the rails documentation
A trivial example is doing the below in your routes.rb
get ':users/:show/:username', controller: "users", action: "show"
and in your UsersController you have a show action that looks like this:
def show
if params[:id].present?
#user = User.find(params[:id])
elsif params[:username].present?
#user = User.find_by(username: params[:username])
end
end
This way you support showing by id and username, if you want do disable support for either of them, modify the if clause as you wish
I think you are looking to change the to_param method like so:
class User < ActiveRecord::Base
def to_param
"#{id} #{name}".parameterize
end
end
This would give the url as: /user/id-name. If you want to get rid of the id before the name it gets a little more complicated. If you were just to remove it, it will more than likely break since ActiveRecord needs the id first for finds.
To get around this I would suggest using FriendlyId gem: https://github.com/norman/friendly_id
There is also a RailsCast showing how to use Friendly_id but its pretty straight forward.
The routes does not care if it is an ID or username.
It is really how you find it in the controller.
Just in the user show controller:
def show
#user = User.find_by_username params[:id]
end
I'm trying to include a few other recent articles when someone views a particular article in my Rails app.
I have the following method in my controller:
def show
#article = Article.find(params[:id])
#recents = Article.where(!#article).order("created_at DESC").limit(4).offset(1)
end
As the expert eye might see, #recents isn't correct. It's my best guess. :)
How do I show some recent articles but not repeat the one they are currently viewing?
You should use a scope in the model, for it has a lot of advanteges. Learn about scopes here. Your case should be something like this:
In the model:
class Article < ActiveRecord::Base
scope :recent, ->(article_id) { where.not(id: article_id).order(created_at: :desc).limit(4) }
end
and in the controller:
def show
#article = Article.find(params[:id])
#recent = Article.recent(#article.id)
end
This way the recent scope will always get the four last articles leaving out the article you pass in as an argument. And scopes are chainable so you could do something like this as well:
def some_action
#user = User.find(params[:user_id])
#user_recent_articles = #user.articles.recent(0)
end
You are getting the user recent articles. I pass a zero because the scope asks for an argument. You could create a different scope if you want to do it the cleanest way.
This, assuming a user has_many articles.
Well, hope it helps!
try with #recents = Article.where.not(id: #article.id).order("created_at DESC").limit(4)
click here - section 2.4 :).
I think there is a way of only making one call instead of 2 the way you have it now.