Rails new initialized object create an empty record - ruby-on-rails

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.

Related

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.

Undefined method `each' for nil:NilClass for show action

I cannot seem to find the problem. In my venues show template, I want to show the venue name, and under that, I list all the venues in the database
<%= venu.name %>
<% #venus.each do |v| %>
I get the error that #venus is nil... but it is defined in my controller:
undefined method 'each' for nil:NilClass
venues_controller.rb
class VenuesController < ApplicationController
before_action :find_venue, only: [:show, :edit, :update, :destroy]
def index
#venus = Venue.all
end
def show
render :layout => nil
#venus = Venue.all
end
def new
#venu = Venue.new
end
def create
#venu = Venue.new(venue_params)
#venu.save
end
def edit
end
def update
end
def destroy
end
private
def venue_params
params.require(:venue).permit(:name, :phone, :address, :description, :type)
end
def find_venue
#venu = Venue.find(params[:id])
end
end
I have a resources :venues route in my routes.rb.
I am not sure what is causing this problem.
In your show method, you should render at the end
def show
#venus = Venue.all
render :layout => nil
end
Remove render :layout => nil from your show action.
And in your view, you need to use the instance variable #venu instead of venu
<%= #venu.name %>
I wonder why you use two instance variables for action show
#venu (via find_venue before_filter) & #venus via the action itself.
Best practice would be removing this line from action show, since show action normally used to show details for one element from a list.
#venus = Venue.all
and use #venu set by the before_filter instead.
But if you do want to keep both then re-order the lines in show action
#venus = Venue.all
render :layout => nil
Also, change the venu to #venu in the show.html.erb and if you like correct the typo in the instance variables #venu => #venue :) (Could happen to any of us)
Usually in the index method, it should show all the venus, and in the show method it would show detailed view of each venue.
Try setting something like this:
def index
#venus = Venue.all
end
def show
render :layout => nil
#venue = Venue.find(params[:id])
end
now in show.html.erb you should be able to use
#venue.name
and in your index.html.erb, you can iterate over the venus like so:
<% #venus.each do |v| %>
<%= link_to v do %>
<%= v.name %>
<% end %>
<% end %>
The above answer is correct. You can use #venus = Venue.all in your show view but because you render first it throws you an error. Just render at the end.

rails routing list filter from html

So i want from this dropdown, to be able to select or basically filter, some drawings based on the workcategory they are at. Both models (Drawings and Workcategories) are connected with has_many drawings and belongs_to workcategories. The drawing holds the workcategory.id as foreign key, and i want to filter by it.This is what i've tried to build
<% #workcategories.each do |workcategory| %>
<li><%= link_to workcategory.name, workcategory_list_path(workcategory.id) %></li>
<% end %>
While in the drawings controller i've added these :
before_filter :list
def list
#drawingsz = Drawing.order("drawings.id ASC").where(:workcategory_id => #workcategory.try(:id))
end
The problem is, whenever i press one of the workcategories inserted, it shows me all the drawings. I've tried from this basic variation to others, including some routings. Oh and talk about routings, i have this :
resources :workcategories do
get :list, :controller => :drawings
resources :drawings
end
resources :drawings
any ideas ? i've browsed the forums a lot last night, and also the routes pages on the official docus, but i just can't get myself around it. I would like the pages to be basically like : /workcategory.name/drawings. Thank you !!
update - added controller
class DrawingsController < ApplicationController
before_action :find_drawing, only: [:edit, :update, :destroy]
before_filter :list
def index
#drawings = Drawing.all.order("created_at DESC")
end
def new
#drawing = Drawing.new
end
def create
#drawing = Drawing.new(drawing_params)
if #drawing.save
flash[:notice] = "Drawing created"
redirect_to(:action=>'index', :drawing_id => #drawing.id)
else
#drawings = Drawings.order()
render('new')
end
end
def edit
end
def update
if #drawing.update_attributes(drawing_params)
flash[:notice] = "Drawing updated."
redirect_to(:action=>"index")
else
render("edit")
end
end
def destroy
#drawing.destroy
redirect_to drawings_path
end
def list
#drawingsz = Drawing.order("drawings.id ASC").where(:workcategory_id => #workcategory.try(:name))
end
private
def find_drawing
#drawing=Drawing.find(params[:id])
end
def drawing_params
params.require(:drawing).permit(:name, :description, :image)
end
end
View update
This is the new view update - which doesn't work, due to another error.
<% #workcategories.each do |workcategory| %>
<li><%= link_to workcategory.name, {:controller => 'drawings', :action => 'list', :workcategory_id => workcategory.id } %></li>
<% end %>
I've updated the list to this :
def list
#workcategory_id = params[:workcategory_id]
if #workcategory_id
#drawingz = Drawing.find(params[:workcategory_id]).workcategory_id
end
end
Now i get the error
Couldn't find Drawing with 'id'=2015
Which is odd, because 2015 is in the names, not in the ids. I feel i'm much closer now but something is still missing ....
Here is cleaned up version of what you are trying to do:
routes.rb
resources :workcategories do
get :drawings, to: 'drawings#workcategory_drawings'
end
resources :drawings
This gives you:
/workcategories/:workcategory_id/drawings => drawings#workcategory_drawings
drawings_controller.rb
..
def workcategory_drawings
#drawings = Drawing.where(workcategory_id: params[:workcategory_id]).all
end
..
(no need for before_action :list)
The dropdown (assuming that you have initialized #workcategories somewhere)
<% #workcategories.each do |workcategory| %>
<li>
<%= link_to workcategory.name, workcategory_drawings_path(workcategory) %>
</li>
<% end %>

Issue with setting up replies in Rails 4

So I am in the process of setting up a forum and everything is setup/working well except for my replies are not appearing on the thread "show" page. After checking the rails console, I see they are saving but the user_id and discussion_id are no. The user_id is always nil and the discussion_id is always 0. The discussion threads were easier to setup but with having these replies, I obviously seem to be having an issue. Here are my snippets of code:
class PostsController
# ...
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
def create
#post = #discussion.post.new(create_params) do |post|
post.user = current_user
end
if #post.save
redirect_to #discussion, notice: "It has been posted!"
else
render :new
end
end
def destroy
#post = #discussion.posts.find(params[:id])
#post.destroy
flash.notice = "Deleted"
redirect_to discussion_path(#discussion)
end
private
def create_params
params.require(:post).permit(:reply)
end
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end
class DiscussionsController
def show
#discussion = Discussion.friendly.find(params[:id])
#post = Post.new
render :layout => 'discussion'
end
end
Partial rendered to reply:
<h2>Reply</h2>
<%= form_for [ #discussion, #post ] do |f| %>
<p>
<%= f.label :reply, "Reply" %><br/>
<%= f.text_field :reply %>
</p>
<p>
<%= f.submit 'Submit' %>
</p>
<% end %>
Partial rendered to show replies in on discussion page:
<h3><%= post.user.first_name %></h3>
<%= post.reply %>
Posted: <%= post.created_at.strftime("%b. %d %Y") %></p>
<p><%= link_to "Delete Comment", [post.discussion, post], data: {confirm: "Are you sure you wish to delete?"}, method: :delete, :class => "post_choices" %></p>
Just want to mention that I also have the correct associations between the three models (User, Discussion, Post). If there is more code needed, please let me know. I appreciate it very much for any information that may be helpful =)
Joe
EDIT
class User < ActiveRecord::Base
has_many :articles
has_many :discussions
has_many :posts
# ...
end
class Discussion
belongs_to :user
has_many :posts
extend FriendlyId
friendly_id :subject, use: :slugged
end
class Post
belongs_to :discussion
belongs_to :user
end
I could post the entire user model if needed but its all validations/devise aspects =P The other two I listed all of the contents in the models.
Edit 2
Thanks to Max, the user_id returns correctly in the console but still not the discussions. Going go dig around a bit more with the recent changes to see what else =)
There are a few issue you need to deal with.
First you should ensure that Devise is actually authorizing your controller action.
class PostsController < ApplicationController
before_filter :authenticate_user!
end
Otherwise current_user will return nil if there is no signed in user. And I'm
guessing that you do not want un-authenticated users to be able to create posts.
Also if you have a nested route you most likely want to check that the discussion actually
exists before trying to add posts.
class PostsController
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
private
# Will raise an ActiveRecord::NotFoundError
# if the Discussion does not exist
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end
When you are creating resources be careful not to query the database needlessly.
This especially applies to CREATE and UPDATE queries which are expensive.
def create
#post = Post.create(post_params) # INSERT INTO 'users'
#post.discussion_id = params[:discussion_id]
#post.user = current_user
#post.save # UPDATE 'users'
flash.notice = "It has been posted!"
redirect_to discussions_path(#post.discussion)
end
Also you are not even checking if the record was created successfully.
So lets put it all together:
class PostsController
before_filter :authenticate_user!
before_filter :set_discussion, only: [:new, :create, :destroy]
def new
#post = #discussion.post.new
end
def create
# new does not insert the record into the database
#post = #discussion.post.new(create_params) do |post|
post.user = current_user
end
if #post.save
redirect_to #discussion, notice: "It has been posted!"
else
render :new # or redirect back
end
end
def destroy
#post = #discussion.posts.find(params[:id])
#post.destroy
flash.notice = "Deleted"
redirect_to discussion_path(#discussion)
end
private
def create_params
# Only permit the params which the user should actually send!
params.require(:post).permit(:reply)
end
# Will raise an ActiveRecord::NotFoundError
# if the Discussion does not exist
def set_discussion
#discussion = Discussion.friendly.find(params[:id])
end
end

How to redirect from a child class to the parent class#show view?

I have an application where an Event has_many Videos, which belongs_to an Event. How can I click on an instance of the video and have it redirect to the events#show view using it's event_id foreign key to identify the correct event? The association definitely works, but I'm struggling to think through the logic, or even where the redirect should take place i.e. in the routes, the controller or the view?
Here's some code which may be relevant:
From the view
<% #video.each do |v| %>
<li><%= link_to image_tag(v.video_thumb('150'), width: 150), v %></li>
<% end %>
class VideosController < ApplicationController
def show
#video = Video.find(params[:id])
if current_event
#videos = current_event.videos
#random_video = current_event.videos.random
else
#videos = Video.all
#random_video = Video.random
end
end
class EventsController < ApplicationController
def show
#event = Event.find_by_name(request.subdomain)
if request.params[:id].present?
#event = Event.find(params[:id])
end
if not #event.nil? and #event.videos.any?
#video = Video.random(#event)
#comment = #video.comments.new
#comments = #video.comments
end
end
resources :events do
match '', to: 'events#show', constraints: lambda { |r| r.subdomain.present? && r.subdomain != 'www' }
end
resources :videos
Let me know if there is any other code you might need to help and I will update the question with the relevant code.
Thanks for any help....
You can use event_path(#event.video)
Let me tell you that, when a video belongs to event, you can easily find the association event related to that video will be easily found from that object. For example:
#video = Video.find(params[:id])
#event = #video.event
for link generation simply: link_to #event.name, event_path(#event)
// if there is any subdomain for that event and if subdomain define the
EventsController#show as root then to go to that event show page we can write:
link_to "#{#video.event.name}", root_url(:subdomain =>
#video.event.name)
or same:
link_to "#{#event.name}", root_url(:subdomain => #event.name
I hope this will work!

Resources