I think I am stumbling upon a rendering/redirection issue. In my rails application I have clients who can have many projects. When I update a project I use a nested form with the form_for method, passing it a client and project instance variable, respectively.
Here is my code:
routes.rb:
resources :clients do
resources :projects
end
projects_controller.rb
def edit
#client = Client.find(params[:client_id])
#project = Project.find(params[:id])
end
def update
#project = Project.find(params[:id])
respond_to do |format|
if #project.update_attributes(params[:project])
format.html { redirect_to client_project_path(#project.client, #project), notice: 'Project was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" } # !!this seems to be the problem
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
views/projects/_form.html.erb:
<%= simple_form_for([#client, #project]) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :title %>
<%= f.input :start_on %>
<%= f.input :end_on %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
When I simply display the edit action, everything works fine. If I update the project with valid data, everything is fine, too. The problem occurs when I type in invalid or missing data into the form - then the "else part" of the update method is called, and the action "edit" should be rendered.
This indeed happens, but it seems to me that something is wrong either with the "edit" method or my form, because when I change render action: "edit" to redirect_to action: "edit", it does redirect me (of course without error messages so it is clearly not the desired solution, but I just wanted to test).
When "edit" is rendered, this is the error message I am getting:
undefined method `project_path'
which is only referring to the first line in my form.
What am I doing wrong here? Sorry for the long question and thanks for any help!
You need to load your #client variable in the update action. Think of it this way, if you hit the else, what did the edit provide to the view, because you need just that when you render instead of redirect. Being that your resources are nested, the form_for helper needs both the client and the project.
Related
For the moment I've got two models in my app.
class Budget < ApplicationRecord
belongs_to :project
end
class Project < ApplicationRecord
has_one :budget
end
So each project has a budget, but a budget could be used later for different class, so it's not especially a "child" of a project.
I tried differents things for the form, here is the one that works the most for the moment :
<%= simple_form_for #project do |f| %>
<%= f.error_notification %>
<%= f.input :name %>
<%= simple_fields_for #budget do |ff| %>
<%= ff.input :amount %>
<% end %>
<%= f.submit "Create", class: "btn" %>
<% end %>
I tried to replace simple_fields_for by simpleform_for but when I'm doing so when I click "Submit" button, nothing happens.
With that version when I click Submit there is this error :
"undefined method `model_name' for nil:NilClass"
I don't really understand how I'm not working on an existing class here.
Here's my conroller
def create
respond_to do |format|
#budget = Budget.new(params.require("budget").permit(:amount))
if #budget.save # If budget is saved
puts "Budget successfuly created"
#project.budget = #budget
if #project.save
puts "project successfuly created"
else
puts "Unable to create project"
format.html { render :new, notice: 'Project went wrong' }
end
format.html { redirect_to projects_path, notice: 'Success' }
else # If budget isn't saved
puts "Unable to create budget"
format.json { render json: #budget.errors, status: :unprocessable_entity }
format.html { render :new, notice: 'Budget went wrong' }
end
end
I'm searching for good practice concerning simpleform and rails coding, but I can't really find something that is related to my case, or at least I can't recognize what documentation relates to my case.
PS : In my rails console, I've got that output :
Unable to create budget
Your controller is a bit hacky. I would look into the default generated create method by the scaffold generater
To get a good idea of how you could clean it up. That would mean a better chance for debugging.
Nested params
One thing i do see is, that something is wrong in your param call.
#budget = Budget.new(params.require("budget").permit(:amount))
This is like saying: Find the param 'amount' for budget.
But your params are generated differently by your form:
You have a simple form for #projects with a simple field for #budget.
Therefore your params will be nested like this:
params[:project][:budget]
Hope it helps.
By the way: Do you have a before_action to set #project?
Or else that would return nil on line 6 in your controller.
I'm currently trying to implement polymorphic comments within my app, but I'm running into problems with converting my partial form.
I was following this tutorial, step-by-step-guide-to-polymorphic-associations-in-rails, but it didn't go over this section.
Mainly, I have an Image that is commentable, and a partial at the bottom to allow users to comment on the Image.
However, when submitting the form, it can't find the #commentable object as params[:id] and params[:image_id] are both nil.
I'm having problems understanding how I'm supposed to pass this information, as the partial knows this information, but the controller does not.
// images/show.html.erb
<div class="container comment-form" >
<%= render 'comments/form', comment: #image.comments.build %>
</div>
// comments/_form.html.erb
<%= bootstrap_form_for(comment) do |f| %>
<%= f.text_area :message, :hide_label => true, :placeholder => 'Add a comment' %>
<%= f.submit 'Reply', :class=> 'btn btn-default pull-right' %>
<% end %>
// comments_controller.rb
def create
#commentable = find_commentable
#comment = #commentable.comments.build(comment_params) <<<<<
respond_to do |format|
if #comment.save
format.html { redirect_to (comment_path #comment), notice: 'Comment was successfully created.' }
format.json { render :show, status: :created, location: #comment }
else
format.html { render :new }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
Error on #comment = #commentable.comments.build(comment_params)
undefined methodcomments' for nil:NilClass`
I also noticed that there is no id in the request parameters.
Parameters:
{"utf8"=>"✓", "authenticity_token"=>"xxxxxx", "comment"=>{"message"=>"nice photo"}, "commit"=>"Reply"}
Thanks for your help.
When you pass a record to a form builder rails uses the polymorphic route helpers* to lookup the url for the action attribute.
To route to a nested resource you need to pass the parent and child(ren) in an array:
bootstrap_form_for([#commentable, #comment])
# or
bootstrap_form_for([#comment.commentable, #comment])
This would give the path /images/:image_id/comments for a new record and /images/:image_id/comments/:id if it has been persisted.
You are attempting to build your comment twice. Once in the show.html, with comment: #image.comments.build and then again in your create method with #comment = #commentable.comments.build(comment_params) <<<<<
The tutorial you linked to included the private method below. If your goal is to create a comment that belongs to your Image object, the method below would look for a param with your image_id, and would return Image.find(params[:image_id])
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
You could change your show.html to pass in your image_id as a hidden param with:
<div class="container comment-form" >
<%= render 'comments/form', image_id: #image.id %>
</div>
I m using hidden field in my app to add user_id to my database "Camping". I have associations "User" has many campings and "Camping" belongs_to "user".
When I run firebug or something like this, I can modify user_id value of this field. If any user puts his ID, I can modify object to other user... I want to avoid this !
My code
<%= f.hidden_field :user_id, :value => current_user.id %>
This code, is necessary because I allow only user to edit / updated / create object if they have user_id == current_user.id.
How to fix this security problem ?
By the way, I m using devise.
Edit with full code
My _form.html.erb
<%= form_for(camping) do |f| %>
<% if camping.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(camping.errors.count, "error") %> prohibited this camping from being saved:</h2>
<ul>
<% camping.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<div class="form-group">
<label for="name">Nom du camping</label>
<%= f.text_field :name, autofocus: true, class:"form-control", id:"name", :required => true%>
</div>
<div class="actions">
<%= f.submit "Enregistrer", class:"btn btn-success" %>
</div>
<% end %>
my controller
def new
#camping = Camping.new
#campings = Camping.all
end
def edit
end
def create
#camping = Camping.new(camping_params)
respond_to do |format|
if #camping.save
format.html { redirect_to #camping, notice: 'Camping was successfully created.' }
format.json { render :show, status: :created, location: #camping }
else
format.html { render :new }
format.json { render json: #camping.errors, status: :unprocessable_entity }
end
end
end
def update
#camping = Camping.find(params[:id])
respond_to do |format|
if #camping.update(camping_params)
format.html { redirect_to #camping, notice: 'Camping was successfully updated.' }
format.json { render :show, status: :ok, location: #camping }
else
format.html { render :edit }
format.json { render json: #camping.errors, status: :unprocessable_entity }
end
end
end
my edit.html.erb
<div class="containershow">
<h1>Editing Camping</h1>
<%= render 'form', camping: #camping %>
<%= link_to 'Show', #camping %> |
<%= link_to 'Back', campings_path %>
</div>
my new.html.erb
<h1>New Camping</h1>
<%= render 'form', camping: #camping %>
<%= link_to 'Back', campings_path %>
Edit solution ?
User can create and update his camping. I delete hidden_field
def create
# #camping = Camping.new(camping_params)
#camping = Camping.new((camping_params).merge(:user_id => current_user.id))
respond_to do |format|
if #camping.save
format.html { redirect_to #camping, notice: 'Camping was successfully created.' }
format.json { render :show, status: :created, location: #camping }
else
format.html { render :new }
format.json { render json: #camping.errors, status: :unprocessable_entity }
end
end
end
In Devise, the current user object is in current_user, available to your controllers. When saving the model, make sure to fill the user id field from that object, and not user input in the update action of your controller. Note that the edit action does not matter, that just renders the edit page, the actual update happens in update (if you follow the default conventions). Of course if you don't want users to even see other users' objects, you also need access control in other controller actions like edit as well, but that (implementing access control in a multi-tenant Rails application) is a different and much broader question.
More generally, be aware that anything that comes from a request can very easily be forged by a user. Always implement security server-side and do not trust user input!
Edit (seeing your code)
To prevent users updating others' Campings, you need to check in update after getting the #camping object (the second line) whether that's a camping object that your logged on user (current_user.id) is supposed to be able to edit.
The same way, if you want to prevent users from creating Campings for other users, you need to make sure in create that user_id will be set to the current user, something like #camping.user_id=current_user.id.
Similarly, if you want to prevent having a look at each other's Campings, you need to add checks to edit, show and pretty much all actions that return such objects.
There are gems like cancan and cancancan that may help with access control in Rails, they are worth a look!
Your Question is quite interesting but simple In the any HTML View Any one can change anything this will cause a security wise vulnerability as well.
To avoid these issues we need to authenticate it by two way You have to check the code by like It should be use by Controller not by view.
Suppose If you are creating any article of particular user
So To avoid it what you can do You can set the User ID in Session and make a Helper Method to find Current User always
So that you can find current user directly from controller and create article according to user
def Create
#article = current_user.articles.create(article_params)
end
This kind of Two way checking you can put up so that It will be safe.
To avoid the spend time on these work you can use gem directly like Devise
I am new to Ruby-on-rails and I am currently working on a project that let a user log in to add,create update delete a Marvel character. Each characters have a name, description, origin, alliance and image.
I used Carrierwave for file upload.
I used the scaffold command and everything was working fine, until I decided to be able to create and update my characters on the same page using .js.erb files instead of having to redirect the user to 2 different pages for the create and the update.
I have the following error everytime I try to create a character with a image. everything works fine when I don't add a image:
ActionController::InvalidAuthenticityToken
I know that there are a few different other similar questions already asked on the forum but I can't seem to find the answer to my problem.
I am using Rails 4.2.6.
I tried to add the gem remotipart but it didn't fix my issue.
create.js.erb code:
$("#characters").append("<%= escape_javascript(render #character)%>");
create action in the controller:
def create
#character = Character.new(character_params)
respond_to do |format|
if #character.save
format.html { redirect_to #character, notice: 'Character was successfully created.' }
format.json { render :show, status: :created, location: #character }
format.js
else
format.html { render :new }
format.json { render json: #character.errors, status: :unprocessable_entity }
end
end
end
I hope I provide enough information, thanks in advance!
Edit:
Here is the code I have in the form.html.erb that let the users add a image:
<div class="field">
<%= f.label :image %>
<%= f.file_field :image %>
<% if f.object.image %>
<%= image_tag f.object.image.url %>
<!--<%= f.label :remove_image %>
<%= f.check_box :remove_image %> -->
<% end %>
</div>
I think your Rails forms now will not render the CSRF field in the form:
<%= form_for #character, :remote => true, :authenticity_token => true,:multipart => true do |f| %>
.....
<% end %>
skip_before_action :verify_authenticity_token
Put this in your controller
In my views, I have comments/_form.html.erb, and there is another controller views for post posts/show.html. I want to render comments/_form.html.erb in posts/show.html, which shows an individual post and comments associated with that post, and it is all set and working fine.
I want to provide ability to comment and render that partial below these comments so that we can make new comment on the same posts/show.html page instead of navigating to comments/new.html.erb page. I am using nested resources like:
resources :posts do
resources :comments
end
In my comments/new.html.erb, I am doing this:
<%= form_for([:post, #comment]) do |f| %>
....
<% end %>
How should I render it in my posts/show.html.erb page?
Javascript is the missing piece in your puzzle. Essentially you'll do 3 things.
Make sure your form is using js to submit (remote)
Make sure your controller responds to js input
Create a js.erb file that will append each new comment
Implementation notes:
I would probably do something like this:
Add the form to the view, and add an element that wraps around the comments, in this case, I used ul#comments
# app/views/posts/show.html.erb
<p>
<strong>Title:</strong>
<%= #post.title %>
</p>
<p>
<strong>Body:</strong>
<%= #post.body %>
</p>
<br/>
<ul id="comments">
<%= render #comments %>
</ul>
<%= form_for [#post, #new_comment], remote: true do |f| %>
<%= f.text_field :body %>
<%= f.submit %>
<%end%>
Add the js response in the controller
# app/controllers/comments_controller.rb
def create
#post = Post.find params[:post_id]
#comment = #post.comments.new(comment_params)
#new_comment = #post.comments.new(comment_params)
respond_to do |format|
if #comment.save
format.html { redirect_to [#post, #comment], notice: 'Comment was successfully created.' }
format.json { render :show, status: :created, location: #comment }
format.js
else
format.html { render :new }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
Then add a ujs file to make your changes on the fly (this does 2 things, adds a new comment and empties the form).
# app/views/comments/create.js.erb
$('#comments').append("<%= escape_javascript(render partial: '/comments/comment', locals: { comment: #comment } ) %>");
$('form.new_comment').find("input#comment_body").val('');
Also to note::
I'm using #new_comment so there is no interference when you render a newly created comment
You'll need to add #new_comment in 2 places, first in your posts show action and also in your comments create action