I received help Setting up a polymorphic association
I am now having trouble implementing the submit form and create action. A user just needs to follow and unfollow each model.
In my Follow Controller
class FollowsController < ApplicationController
before_filter :find_supports
def create
#relation = find_supports
#follow = #relation.find(params[:follow])
current_user.follows.follow!(#follow)
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
def destroy
#follows = Follow.find(params[:id]).followable
current_user.unfollow!(#follows)
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
private
def find_supports
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
else
nil
end
end
end
end
In my Follow Form, which is rendered at on my polymorphic models
EDIT
<%= form_for #cause.follows.build(params[:follow]) do |f| %>
<div><%= f.hidden_field :followable_id %></div>
<div><%= f.hidden_field :followable_type %></div>
<div><%= f.hidden_field :follower_id %></div>
<div class="create-button"><%= f.submit "Follow" %></div>
<% end %> <!-- I now get the values for followable_id and followable_type, but it
wont get grab the follower_id and I get an error in the create action -->
and in my User Model, I have these 3 methods
def following?(follow)
follows.find_by_followable_id(follow)
end
def follow!(followable)
follows.create![] ##I have tried many different params here
end
def unfollow!(followable)
follows.destroy(params[:followable_id]).destroy
end
I cannot get the follower_id, followable_id and followable_type to save properly. Any suggestions?
Thanks in advance, I have spent many hours with this.
Here is how I solved my issues. The answer is using AJAX and Devise helper method current_user
Form Partial
<%= form_for #relation.supports.build(params[:support]), :remote => true do |f| %>
<div><%= f.hidden_field :supporter_id, :value => current_user.id %></div>
<div><%= f.hidden_field :supportable_id %></div>
<div><%= f.hidden_field :supportable_type %></div>
<%= f.submit "Support", :class => "support-button" %>
<% end %>
My Controller
class SupportsController < ApplicationController
before_filter :find_relation
def create
#relation.supports.create!(params[:support])
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
def destroy
#relation.supports.find(params[:id]).destroy
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
private
def find_supports(instance_params)
class_name = instance_params["supportable_type"].classify.constantize
class_name.find(instance_params["supportable_id"])
end
def find_relation
#relation = find_supports(params[:support])
end
end
Also, add a before_filter in all the controllers that render the form to find the correct object. This will allow you use the same form partial with all your polymorphic classes.
def find_supports
#relation = YourClass.find(params[:id])
end
Related
I have 2 models - project and todo, and the project contain many todos. I'm trying to write a form that would add a todo to a specific project, that's selected in the tray.
Unfortunately I get an error
Couldn't find Project with 'id'=
from todos_controller when i redirect to /todos
#project = Project.find(params[:project_id])
/projects/index.html.erb
<h1> Задачи </h1>
<% #projects.each do |project_name|%>
<h3><%= project_name.title %></h3>
<ul>
<% project_name.todos.each do |project_todo| %>
<li>
<p><%= project_todo.text %></p>
</li>
<% end %>
</ul>
<% end %>
<h1> Новая задача </h1>
<%= form_with scope: :todo, url: todos_path, local: true do |form| %>
<p>
<% form.label :text %><br>
<%= form.text_field :title, placeholder: "Название задачи" %>
</p>
<%= form.select( :project_id, options_from_collection_for_select(Project.all, :id, :title)) %>
<p>
ОТМЕНА
<%= link_to form.submit %>
</p>
<% end %>
projects_controller.rb
class ProjectsController < ApplicationController
def index
#projects = Project.all
respond_to do |format|
format.html
format.json {render json: #projects}
end
end
todos_controller.rb
class TodosController < ApplicationController
def index
#todos = Todo.all
respond_to do |format|
format.html
format.json {render json: #todos}
end
end
def update
end
def create
#project = Project.find(params[:project_id])
#todo = #project.todo.create(todo_params)
redirect_to projects_path
end
end
routes.rb
Rails.application.routes.draw do
resources :projects, :todos
root 'projects#index'
end
I also could not display the submit button as a text link.
modify create action as
def create
#project = Project.find(params[:todo][project_id])
#todo = #project.todo.create(todo_params)
redirect_to projects_path
end
if not works then check params hash from server log and check project_id is present in params or not.
Above will work but I think you do not need to find project if you modify create action as
def create
#todo = Todo.create(todo_params)
redirect_to projects_path
end
also define method as
def todo_params
params.require(:todo).permit(:project_id, :title)
end
Your controller expects a project_id parameter which is probably not passed! Try to add the project id to your params hash when you trigger the todos#create method
params[:project_id] is nil because the form you are submitting for todo, so you need to access project_id as
Project.find(params[:todo][project_id]) in create action.
So replace your create action as follow
def create
#project = Project.find(params[:todo][project_id])
#todo = #project.todo.create(todo_params)
redirect_to projects_path
end
Dont forget to include project_id in todo_params method.
I'm trying to create a comment on a video show page. When I submit the form, rails gives me a flash notice: "User must exist, Video must exist". Not sure why my strong params aren't going through to the create method.
comments_controller.rb
def create
#user = current_user
#video = Video.find(params[:video_id])
#comment = Comment.new(comment_params)
#post = #video.post
if #comment.save
flash[:notice] = "Comment successfully created"
redirect_to post_video_path(#post, #video)
else
#errors = #comment.errors.full_messages.join(', ')
flash[:notice] = #errors
render :'videos/show'
end
private
def comment_params
params.require(:comment).permit(
:body,
:user,
:video
)
end
models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :video
validates :body, presence: true
end
models/video.rb
class Video < ActiveRecord::Base
belongs_to :user
belongs_to :post
has_many :comments
end
views/videos/show.html.erb
<%= #video.title %>
<%= content_tag(:iframe, nil, src: "//www.youtube.com/embed/#{#video.embed_id}") %>
<%= link_to "Delete Video", post_video_path(#post, #video), method: :delete %>
<%= link_to('Back', user_post_path(#user, #post)) %>
<h3>Comments</h3>
<%= form_for [#video, #comment] do |f| %>
<%= f.label(:body, "Comment") %>
<%= f.text_area(:body) %>
<%= f.submit("Submit Comment") %>
<% end %>
<% unless #comments.nil? %>
<% #comments.each do |comment| %>
<%= comment.body %>
<%= comment.user %>
<% end %>
<% end %>
I tried adding this to the create method...
#comment.user = current_user
#comment.video = #video
That allowed the comment to save but instead of displaying the comment.body, it displayed the comment object. It still doesn't explain why the strong params aren't being passed.
This is probably a nested params issue and how you have the strong params defined. Check this answer for more information.
If you need to see what is in the params, insert a pry statement into the controller and inspect it there.
Good luck!
There are multiple things you should look at. I have made multiple changes to your code. Go through them.
In your videos/show.html.erb
<%= form_for Comment.new do |f| %>
<%= f.label(:body, "Comment") %>
<%= f.text_area(:body) %>
<%= f.hidden_field :video_id, :value => #video.id %>
<%= f.submit("Submit Comment") %>
<% end %>
Send video_id using hidden_field. Do not send current user id for security reasons. If you take current user id from form end user can easily edit your form in html and pass someone else's user id and this will be one of the easiest and major vulnerability.
In comments_controller.rb
def create
#comment = Comment.new(comment_params)
#comment.user = current_user # we are making sure that current_user is set to comment.
if #comment.save
flash[:notice] = "Comment successfully created"
redirect_to post_video_path(#comment.video.post, #comment.video)
else
#errors = #comment.errors.full_messages.join(', ')
flash[:notice] = #errors
render :'videos/show'
end
end
private
def comment_params
params.require(:comment).permit(:body, :user, :video_id)
# We are permitting video_id instead of video
end
I got this to work but I'm not sure if this addresses the security concerns that #Dinesh raised.
comments_controller.rb
def create
#user = current_user
#video = Video.find(params[:video_id])
#comment = Comment.new(comment_params)
#comment.user = #user
#comment.video = #video
#post = #video.post
if current_user == #video.user || current_user.admin
if #comment.save
flash[:notice] = "Comment successfully created"
redirect_to post_video_path(#post, #video)
else
#errors = #comment.errors.full_messages.join(", ")
flash[:notice] = #errors
render :"videos/show"
end
else
flash[:notice] = "Only OP or admin may comment"
render :"videos/show"
end
end
and
private
def comment_params
params.require(:comment).permit(
:body,
)
end
EDIT: I managed to delete! i had to define teh instance variable #movies = Movie.find(params[:id]) to the delete method in the controller.
I still can't update though. I get "param is missing or the value is empty: movie"
I forgot to add my contrller! sorry!
I'm trying to replicate one of my in class exercises into another app, for practice.
The idea is to be able to add new movies into a database, and get the option to update their info, and delete them as well. I can add new content, but I can't update them or delete them.
Appreciate any inputs I can get.
Routes.rb
Rails.application.routes.draw do
root "movies#index"
get "/movies", to: "movies#index"
get "/movies/new", to: "movies#new", as: "new_movie"
get '/movies/:id', to: "movies#movie_page", as: "movie_page"
post "/movies", to: "movies#add"
get "/movies/:id/edit", to: "movies#edit", as: "movie_edit"
put "/movies/:id", to: "movies#update"
patch "/movies/:id", to: "movies#update", as: "update_movie"
delete "/movies/:id", to: "movies#delete", as: "delete_movie"
end
Controller
class MoviesController < ApplicationController
def index
#movies = Movie.all
end
def movie_page
#movies = Movie.find(params[:id])
end
def new
#movies = Movie.new
end
def add
#movies = Movie.create(movie_params)
redirect_to movies_path
end
def edit
#movies = Movie.find(params[:id])
end
def update
#movies.update(movie_params)
redirect_to #movies, notice: "Shirt was updated."
end
def delete
#movies = Movie.find(params[:id])
#movies.destroy
# flash[:notice] = "Shirt was deleted."
redirect_to root_path, notice: "Shirt was deleted."
end
def movie_params
params.require(:movie).permit(:title, :description, :year_released)
end
# def set_movie
# #movies = Movie.find(params[:id])
# end
end
Form partial
<%= form_for #movies do |m| %>
<p>
<%= m.label :title %><br>
<%= m.text_field :title %>
</p>
<p>
<%= m.label :description %><br>
<%= m.text_field :description %>
</p>
<p>
<%= m.label :year_released %><br>
<%= m.text_field :year_released %>
</p>
<p>
<%= m.submit %>
</p>
<% end %>
Movie page html (individual movies, labeled by IDs)**I can't update or Delete, no route matches Delete.
When I press Update - I get param is missing or the value is empty: movie
<h1><%= #movies.title %></h1>
<h2>Released on : <%= #movies.year_released %> </h2>
<p> <%= #movies.description %> </p>
<%= link_to "Update", movie_edit_path(#movies) %>
<%= link_to "Delete", movies_path, method: :delete %
Edit page *I cant access this link. the form is the problem
<h1>Edit <%= #movies.title %> Info </h1>
<%= render "form" %>
<%= link_to "Cancel Edit", movie_edit_path(#movies) %>
Many thanks guys
def update
#movie = Move.find(params[:id])
#movie.update(movie_params)
redirect_to movie_path(#movie)
end
on your routes. all you need is resources :movies
you are getting param is empty because you have to pass in the id of the movie to update.
The major issue is that you do not load the variable #movies from the DB before you use it.
def update
#movies.update(movie_params)
redirect_to #movies, notice: "Shirt was updated."
end
def update
#movies.find(params[:id])
#movie.update(movie_params)
redirect_to #movies, notice: "Shirt was updated."
end
Aside from that you have tons of duplication and quite a few idiosyncrasies.
Rails uses these naming conventions for actions:
index
show (not movie_page)
new
create (not add)
edit
update
destroy (not delete)
You should follow them unless you have a damn good reason not to.
class MoviesController < ApplicationController
# cuts the duplication
before_filter :set_movie, except: [:new, :index]
def index
#movies = Movie.all
end
# GET /movies/:id
def show
end
# GET /movies/new
def new
#movie = Movie.new
end
# POST /movies
def create
#movie = Movie.create(movie_params)
redirect_to movies_path
end
# GET /movies/edit
def edit
end
# PUT|PATCH /movies/:id
def update
#movie.update(movie_params)
redirect_to #movie, notice: "Shirt was updated."
end
# DELETE /movies/:id
def destroy
#movie.destroy
redirect_to action: :index
end
private
def movie_params
params.require(:movie).permit(:title, :description, :year_released)
end
def set_movie
# Use the singular form when naming a variable with a single record
# failure to do so may result in tarring and feathering
#movie = Movie.find(params[:id])
end
end
I'm a Rails newbie, and I have a RoR app that I'm working on, the app is supposed to allow invited guests to RSVP to a wedding.
I have allowed for fields to be dynamically added, in order to include additional guests (family of an invited guest). But when I add the dynamic field and add the names, only the last name is displayed in the index.
Is there anything specific I need to do to render all other names together in a field within the table?
This is my current controller thus far:
class GuestsController < ApplicationController
skip_before_filter :authenticate_user!, only: [:new, :create]
def index
#guests = Guest.all
end
def new
#guest = Guest.new
end
def create
#guest = Guest.all
#guest = Guest.create(guest_params)
if #guest.save
respond_to do |format|
format.html { redirect_to :back, notice: 'Thank you for replying' }
format.js
end
else
respond_to do |format|
format.html { render 'new' }
format.js
end
end
end
def destroy
#guest = Guest.find(params[:id])
#guest.destroy
redirect_to guests_path
end
private
def guest_params
params.require(:guest).permit(:status, :name, :message)
end
end
Place Guest in place of User
users controler
def new
#users = []
5.times do
#users << User.new
end
end
if params.has_key?("user")
User.create(users_params(params["user"]))
else
params["users"].each do |user|
if user["name"] != nil || user["age"] != nil
User.create(users_params(user))
end
end
end
def users_params(my_params)
my_params.permit(:name, :age)
end
users_form
<%= form_tag users_path do %>
<% #users.each do |user| %>
<%= fields_for 'users[]', user do |u| %>
<div class="field">
<%= u.label :name %><br>
<%= u.text_field :name %>
</div>
<div class="field">
<%= u.label :age %><br>
<%= u.number_field :age %>
</div>
<% end %>
<% end %>
<div class="actions">
<%= submit_tag %>
</div>
<% end %>
followed a tutorial to help me create instances within a controller. In other words transactions are created on the envelope controller. Like comments on a blog post.
Everything is working perfectly, but I don't know how to edit a transaction now or destroy one. All I need is to find how to edit an existing thing. Let me show you what I have so far:
in views/envelopes/edit (the form code was copied from where you can create new transactions)
<% #envelope.transactions.each do |transaction|%>
<%= form_for [#envelope, #envelope.transactions.build] do |f| %> <!--??? NEED EDIT INSTEAD OF BUILD ???-->
<%= f.text_field :name, "value" => transaction.name %>
<%= f.text_field :cash, "value" => transaction.cash %>
<%= f.submit "Submit" %>
<% end %>
<%= link_to "Remove", root_path %> <!--??? WANT TO REMOVE TRANSACTION ???-->
<% end %>
in routes.rb
resources :envelopes do
resources :transactions
end
in transaction controller
class TransactionsController < ApplicationController
def create
#envelope = Envelope.find(params[:envelope_id])
#transaction = #envelope.transactions.build(transaction_params)#(params[:transaction])
#transaction.save
#envelope.update_attributes :cash => #envelope.cash - #transaction.cash
redirect_to edit_envelope_path(#envelope)
end
def destroy
# ???
end
def update
# ???
end
def transaction_params
params.require(:transaction).permit(:cash, :name, :envelope_id)
end
end
def update
#transaction = #envelope.transactions.find(params[:id])
if #transaction.update(transaction_params)
redirect to #envelope, notice: 'Transaction was successfully updated'
else
redirect_to #envelope, notice: 'Transaction was not updated'
end
end
def destroy
#transaction.destroy
redirect_to #envelope, notice: 'Text here'
end