How to access child's attribute in index - ruby-on-rails

Micropost has content that I want in the index (#user.micropost.content). I can do this in the show but not in index.
My models:
class User < ActiveRecord::Base
has_many :microposts
end
class Micropost < ActiveRecord::Base
belongs_to :user
end
My user_controller.rb:
def index
#users = User.all
end
def show
#user = User.find(params[:id])
#posts = #user.microposts
end

First you need a little eager loading to avoid N+1 queries
def index
#users = User.includes(:microposts).all
end
Then in the view you just loop normally and you'll be able to access the object
#users.each do |user|
user.microposts.each do |micropost|
micropost
end
end

Try with this code in your users controller:
def index
#users = User.includes(:microposts).all
end
And in your index page you show the microposts using:
<% #user.microposts do |micropost| %>
<p><%= micropost.title%></p>
<% end %>
This will allow you to use your users' microposts in the view and is also a good practice because the query in the database for the microposts will be executed only once.

You will have to loop through #users = User.all and access the microposts from each user that way.
def index
#users = User.all
#users.each do |user|
current_users_microposts = user.microposts
# your code here
end
end
Not sure what your requirements are but you can make an array and append each group of user.microposts onto that array as well, in order to have all of them in one variable.

Related

Rails new initialized object create an empty record

I have two model called TodoList and TodoItem. In the TodoItem index page, i'm showing new form and list of todo items. Everything works perfect But it generate an empty record while in browser.
class TodoItem < ApplicationRecord
belongs_to :todo_list
end
class TodoList < ApplicationRecord
has_many :todo_items, dependent: :destroy
end
controllers have:
class TodoItemsController < ApplicationController
def index
#todo_list = TodoList.find(params[:todo_list_id])
#todo_items = #todo_list.todo_items
#new_todo = #todo_list.todo_items.new
end
def create
#todo_list = TodoList.find(params[:todo_list_id])
#todo_item = #todo_list.todo_items.new(params.require(:todo_item).permit(:description, :complete_at))
if #todo_item.save
redirect_to todo_list_todo_items_path(#todo_list)
end
end
end
index.html.erb
<div>
<div>
<% form_with(model: [#todo_list, #todo_item], local: true) do |f| %>
<% f.text_field :description %>
<% f.submit %>
<% end %>
</div>
<ul>
<% #todo_items.each do |todo_item| %>
<li><%= todo_item.description %></li>
<% end %>
</ul>
</div>
class TodoItemsController < ApplicationController
# use callbacks instead of repeating yourself
before_action :set_todolist, only: [:new, :create, :index]
def index
#todo_items = #todo_list.todo_items
#todo_item = TodoItem.new
end
def create
#todo_item = #todo_list.todo_items.new(todo_list_params)
if #todo_item.save
redirect_to [#todo_list, :todo_items]
else
render :new
end
end
private
def set_todolist
#todo_list = TodoList.find(params[:todo_list_id])
end
# use a private method for your params whitelist for readibility
# it also lets you reuse it for the update action
def todo_list_params
params.require(:todo_item)
.permit(:description, :complete_at)
end
end
You where setting a different instance variable (#new_todo) in you index action. The polymorphic route helpers that look up the route helpers from [#todo_list, #todo_item] call compact on the array. So if #todo_item is nil its going to call todo_lists_path instead - ooops!
You alway also need to consider how you are going to respond to invalid data. Usually in Rails this means rendering the new view. If you are rendering the form in another view such as the index view it can get kind of tricky to re-render the same view as you have to set up all the same data as that action which leads to duplication.
It seems #new_todo has been added to #todo_items somehow in index action:
def index
#todo_items = #todo_list.todo_items
#new_todo = #todo_list.todo_items.new
# The above line has a side effect: #todo_items = #todo_items + [#new_todo]
end
I'm not sure it's a bug or feature from Rails (I use Rails 6.1.1).
For a quick fix, you can change #todo_list.todo_items.new to TodoItem.new.

Only show a user's profile if they are contacts with current user

My Users Controller:
class UsersController < ApplicationController
before_action :is_contact, only: [:show]
def index
#contacts = User.joins(:groups)
.where(groups: {id: current_user.groups})
.where.not(id: current_user).uniq
end
def show
end
private
def is_contact
user = User.find(current_user.id)
if user = !#contacts
flash[:alert] = "Sorry, you don't know each other."
redirect_to root_path
end
end
end
My contacts view, where you can either send a message (working) or view profile (not working):
<% #contacts.each do |contact| %>
<h3><%= contact.name %></h3>
<%= link_to 'view profile', user_path(contact), class: "btn"
<% end %>
It looks like you missed that #contacts is an instance variable and it is not shared across requests. Basically when you try to see if there is contacts in the method is_contact, the #contacts variable is nil.
Show and index are 2 different requests. When you access the index page #contacts variable has been created, but when you click the user link on the index page, then another request to show action is made, and in that request you do not have data that was set for index action. You would need to make a db query to get contacts separately for show action.
class UsersController < ApplicationController
before_action :contacts, only: %i[index show]
before_action :is_contact, only: [:show]
def index; end
def show; end
private
def contacts
#contacts ||= User.joins(:groups)
.where(groups: {id: current_user.groups})
.where.not(id: current_user).uniq
end
def is_contact
user = User.find(params[:id)
unless #contacts.detect { |contact| contact.id == user.id }
flash[:alert] = "Sorry, you don't know each other."
redirect_to root_path
end
end
end
def is_contact
user = User.find(current_user.id)
if user = !#contacts
the conditional is an assignment, to check equality you need to use `==
also you need to check for presence in the list, the #contacts is going to be an array and that will never be equal to a member of the list
You are also checking if the current_user is a contact, i think you need to look for the user that is being viewed
Probably should be something like this. I would pull this is_contact checking out to a plain ruby object so that the concept is clear and external to your controller.
def is_contact
viewed_user = User.find(params[:id])
viewed_user_is_contact = #contacts.any? {|contact| contact == user}
if !viewed_user_is_contact
#flash

Passing instance variable from controller to view in rails

I have a user profile controller called "userinfo" and it's corresponding view. The userinfo index is the root path. In the homepage(which is the userinfo index), I have a link that takes you to the user profile page. It is giving me this error when I click on the image on the view page:
My routes are:
My userinfos_controller:
class UserinfosController < ApplicationController
before_action :find_userinfo, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#userinfors = Userinfo.where(:userinfo_id => #userinformation_user_id)
end
def show
#myvideo = Video.last
end
def new
#userinformation = current_user.userinfos.build
end
def create
#userinformation = current_user.userinfos.build(userinfo_params)
if #userinformation.save
redirect_to root_path
else
render 'new'
end
end
def edit
end
def update
end
def destroy
#userinformation.destroy
redirect_to userinfo_path
end
private
def userinfo_params
params.require(:userinfo).permit(:name, :email, :college, :gpa, :major)
end
def find_userinfo
#userinformation = Userinfo.find(params[:id])
end
end
and my view is:
<%= link_to image_tag("student.png", class: 'right'), userinfo_path(#userinfors) %>
I thought maybe I must include ':index' in the 'before_action :find_userinfo' at the top of my controller. If I do that, the homepage doesn't even load and it gives me this error:
Try below code:
controller
def index
#userinfors = Userinfo.where(:userinfo_id => #userinformation_user_id) #pass id instead of object #userinformation_user_id
end
view
<% #userinfors.each do |u| %>
<%= link_to image_tag("student.png", class: 'right'), userinfo_path(u) %>
<% end %>
Your problem is that you're trying to do perform a lookup based on something that's not an ActiveRecord (database) attribute.
Your root goes to UserinfosController which expects #userinformation_user_id but I can't tell from your code where that comes from.
You need to define your route in order that this will be expecting for an specific param, maybe the user id, and then you're able to add the value within your view, in a link_to helper:
You could modify your routes.rb to expect an id as param:
get '/user_infors/:id', to: 'userinfos#index', as: 'userinfo_path'
Then in your controller, use a find to "find" in the database the user with such id. If you'd like to use where then that would give you a relationship with all the userinfos with the id being passed as param.
If you want so, then use Userinfo.where('userinfo_id = ?', params[:id]):
def index
#userinfors = Userinfo.find(params[:id])
end
And then in your view you can access to #userinfors:
<% #userinfors.each do |user| %>
<%= link_to image_tag 'student.png', class: 'right', userinfo_path(user) %>
<% end %>
I think you could define the index to get all the userinfors and a show method to get an specific one, as you're trying to do.

Extract User's ID and insert into Posts table in ruby on rails

Experimenting with ruby on rails.. I put a new Post form on a users show page.(i.e. 0.0.0.0:3000/users/2) I'm trying to extract the user's id and insert it into a 'user_id' field in the Post table when you create a new post. So when the form is submitted from the user's page, I can link it to the user that wrote it.
models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
before_save :create_user_id
def create_user_id
self.user_id = current_user
end
end
models/user.rb
class User < ActiveRecord::Base
has_many :posts
end
helpers/application_helper.rb
module ApplicationHelper
def current_user
#current_user ||= User.find(params[:id])
end
end
controllers/post_controller.rb
class PostsController < ApplicationController
def new
#post = Post.new
end
def show
#post = Post.find(params[:id])
#page_title = #post.title.capitalize
#author = User.find(#post.user_id)
#author_url = "/users/" + #post.user_id.to_s
end
def create
#post = Post.create(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
# private
private
def post_params
params.require(:post).permit(:title, :body, :user_id)
end
end
The error I get:
Couldn't find User without an ID
Extracted source (around line #15):
#post = Post.find(params[:id])
#page_title = #post.title.capitalize
>>#author = User.find(#post.user_id)
#author_url = "/users/" + #post.user_id.to_s
end
If I test and change my application_helper.rb to this it works, and inserts 2 into the Post's user_id field. The current set up just returns nil
module ApplicationHelper
def current_user
#current_user = 2
end
end
First you want to get the current user, for now you can test using something like this:
#current_user ||= User.find(2)
Note that there will not be an :id param available on a create call, :id refers to a specific member of your resource so in this case if get http://localhost:3000/posts/1 posts would be the resource and 1 would be the param :id so this would not return the current_user you expected.
Then association should do all of the work for you and there is no need for the create_user_id method. All you would have to do is tweak your create method to
#post = current_user.posts.create(post_params)

Rails: Displaying published post by all and unpublished post of current_user

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

Resources