Rails and programming noob here. I am a teacher trying to create a god mode where I can view/edit etc posts of my students, however, students should be only able to view their own posts with this in the posts controller:
def index
#posts = Post.where(user_id:current_user)
end
But how do I create an index for me to see everything?
Create an AdminController with the following index method.
def index
if params[:user_id]
#posts = Post.where(user_id: params[:user_id])
else
#posts = Post.all
end
end
This method will show posts of a certain user if the url parameter user_id is set and shows all posts on the system if the url parameter is not specified.
I would recommend using pagination like kaminari for example.
Related
So currently im trying to make an app where each user has its own todo list with its own index page, which means, everybody is able to visit the user page to see each tasks of the user.
I use devise and created a simple model with a user reference:
rails g model Todo title:string completed:boolean user:references
added of course belongs_to / has_many to todo.rb/user.rb
Now since i want each todo index page to be assiociated with the users todos, i've created a nested resource like so:
resources :users do
resources :todos
end
visiting
http://localhost:3000/users/1/todos/
works fine and shows the index page.
Heres the problem: when i change the number after /users/ to, for example, 2, its still working, even though there is no user with the id of 2.
Any ideas how i can make this dynamic, so that the integer after /users/ represents the user_id? Thought i did it right but i guess im missing something. Thanks in advance!
EDIT:
as requested, TodosController.rb:
class TodosController < ApplicationController
def index
#todos = Todo.all
end
def new
end
def show
#todo = Todo.find(params[:id])
#user = #todo.user
end
def update
end
def edit
end
end
Let look at your index action:
def index
#todos = Todo.all
end
It displays all todos always, because it doesn't know anything about the user.
It should be:
def index
#user = User.find(params[:user_id])
#todos = #user.todos
end
And in the show action you have to find the user at first, in this case you're sure, that the requested todo belongs to the requested user
def show
#user = User.find(params[:user_id])
#todo = #user.todos.find(params[:id])
end
You can refactor out #user = User.find(params[:user_id]) to the before_action callback, because you'll use it in all actions
I'm trying to show all the posts that I like by current user.
I'm using Ruby on Rails, and the gems Devise and "Acts As Votable". I've been following the Acts As Votable guide but can't make it work.
I think this is the way to go:
#post.liked_by #user1
#post.downvote_from #user2
I created a new controller called dashboard:
class DashboardController < ApplicationController
def index
#post = Post.all.liked_by current_user
end
end
But when I run my web I get:
undefined method `liked_by' for #<Post::ActiveRecord_Relation:0x9aa89b0>
What can I do?
The issue is that you're calling it on an ActiveRecord Relation, not on the model object, itself. A couple of minor changes, and you'll be all set.
First, we make the instance variable plural to show that it's receiving multiple records (a user can like multiple posts):
class DashboardController < ApplicationController
def index
#posts = Post.all.liked_by current_user
end
end
Then, to process these, you'll want to navigate to the individual record or records. If you want to process the whole list, you can do this:
#posts.each do |post|
post.liked_by #user1
post.downvote_from #user2
end
This will apply the like and downvote to all of the posts. However, you can update only the first post, like this:
post = #posts.first
post.liked_by #user1
post.downvote_from #user2
If you already knew what post you wanted to vote on (maybe based on which was chosen in the UI), you could do this:
#post = Post.find_by(params[:id])
#post.liked_by #user1
#post.downvote_from #user2
Making sure to keep your plural and singular names distinct will help you keep in tune with Rails conventions and will help you easily identify when you're working with a collection or an individual object.
try #posts = current_user.find_up_voted_items in your controller and putting acts_as_voter in your User model.
Say for example I have two models, posts and category. Now say I want to make it so the from the category show page you can create a new post using the form_for method. To do this, you will obviously need access to the #category variable and a new instance of a post (#post). Is this acceptable code in the controller?
#app/controllers/categories_controller.rb
def show
#category = Category.find(params[:id])
#post = Post.new
end
Or is it bad practice to have two instance variables defined in the one controller action - and if it is, what would be the best practice for a case like this?
I usually do something like:
#app/controllers/categories_controller.rb
helper_method :category
helper_method :post
def show
end
private
def category
#_category ||= params[:id] ? Category.find(params[:id]) : Category.new(params[:category])
end
def post
#_post ||= Post.new(params[:post])
end
Then, in your views, just refer to post or category (not #post or #_post). The nice thing is you can remove the same logic from your new, delete, etc methods...
Actions related to posts should be in the PostsController as much as possible.
Let's say the user is looking at all posts under the category "rails": /categories/rails
There's a button on that page to create a new post under the "rails" category, href: /posts/new?category=rails
This takes you to PostsController#new where you instantiate a new Post, validate the category param and build a view. This view could either be a new page, or a modal popping up.
I try to follow RESTfull approach in controllers, but sometimes I need action which won't fit into default ones. E.g. I have an index action rendering all articles which is
def index
#articles = Article.all
end
but what if I also want search them? should I create separate actions for it, or should I bloat existing controller, like:
def index
if params[:search]
#articles = Article.where(id: params[:search_id])
else
#articles = Article.all
end
end
What is the right way?
You should use same action and create a index action only. And search logic goes to Article model.
You should follow this
I would keep it in index. If you want to keep it clean and standardise it you could change the controller code thus:
def index
#articles = Article.search(params[:search])
end
and then add a class method in Article
class << self
def search(options={})
if options.blank?
return self.all
else
..other search logic
end
end
end
Note that the example you gave for a search doesn't really make sense as it's specifying the id of the article. If you already know the id then that's not really a search: you might as well just go to the show page for the article.
In my Rails 4 project, I have a route like this (via rake routes) :
user_articles_path GET /users/:user_id/articles(.:format) articles#index
This works beautifully so that /users/1/articles shows user 1's articles.
What's the simplest way to get all articles from all users?
Something like /users/*/articles or /users/all/articles would be sweet.
Here's the Articlescontroller index method:
def index
#articles = if params[:user_id]
user = User.find(params[:user_id])
user.articles.where('content like ?', "%#{params[:search]}%").page(params[:page]).per_page(5)
else
current_user.articles.where('content like ?', "%#{params[:search]}%").page(params[:page]).per_page(5)
end
end
I show articles based on a user/id and then if a user is logged in (via Twitter) I show them their articles.
Update
Not sure if this is too hacky, but here's my current index method. It works like I want, but it could probably be simplified:
def index
if current_user
if params[:user_id]
user = User.find(params[:user_id])
#articles = user.articles.where('content like ?', "%#{params[:search]}%").page(params[:page]).per_page(5)
else
#articles = current_user.articles.where('content like ?', "%#{params[:search]}%").page(params[:page]).per_page(5)
end
else
#articles = Article.all.where('content like ?', "%#{params[:search]}%").page(params[:page]).per_page(5)
end
end
Articles are there for logged in and logged out users with easy routing to show a specific user's articles. Suggestions for cleaning it are welcome.
The best way is to use the Article as a resource in your route file:
resources :articles
Doing this you will have to create an ArticleController and with the path /articles you'll be redirected to a new set of views that will load all the articles without handling the user they come from. Eg. in the index action of this controller:
def index
#articles = Article.all
end
This is the best way to handle a REST interface in Ruby, since you are querying the Article resource alone here, and you should not pass through the User resources.