Creating new object with has_and_belongs_to_many association - ruby-on-rails

My rails project consists of three models: User, Groups and Transactions. Groups and Transactions are connected by a has_and_belongs_to_many relationship and both belong to User. I'm trying to implement a feature where you can add new transactions on a groups' show page that will automatically be associated with the show pages' group. I've tried the following but in the transactions controller rails doesn't get which group the transaction is supposed to be associated with.
Groups' show page:
<p id="notice"><%= notice %></p>
<p>
<strong>Name:</strong>
<%= #group.name %>
<% #group.deals.each do |deal| %>
<%= deal.name %>
<%= deal.user.name %>
<%= link_to 'Show', deal %>
<% end %>
</p>
<%= link_to 'Edit', edit_group_path(#group) %> |
<%= link_to 'Back', groups_path %>
<%= link_to 'New Transaction', new_transaction_path(#group) %> |
Group's controller page:
# GET /transactions/new
def new
#transaction = Transaction.new
end
# POST /transactions or /transactions.json
def create
#transaction = Transaction.new(transaction_params.merge(user_id: current_user.id))
respond_to do |format|
if #transaction.save
format.html { redirect_to #transaction, notice: "Transaction was successfully created." }
format.json { render :show, status: :created, location: #transaction }
#transaction.groups << #group
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #transaction.errors, status: :unprocessable_entity }
end
end
end
If I replace #group with f.e. Group.first the association is created - it's the wrong one in most cases ofc tho. Does the solution lie within in the views maybe, can anyone help out?

Related

Form hidden fields and security

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

Does rails form_for use the same route when using a symbol or instance var?

This is just a general question about the helper "form_for". I'm working on a program out of a textbook which has a form file using <%= form_for(#product) do |f| %>. The form is shared by the new and edit template. However, I've seen a lot of tutorials using a symbol (:product) instead of the instance variable. So, I tried swapping them to see what would happen. As it happens it gives me a routing error when trying to submit a form:
No route matches [POST] "/products/new"
and
No route matches [POST] "/products/5/edit"
Here's the code:
<%= form_for(:product) do |f| %>
<% if #product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.errors.count, "error") %> prohibited this product from being saved:</h2>
<ul>
<% #product.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.text_field :price %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
and in the product_controller
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: #product }
else
format.html { render :edit }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
I thought I had read that "form_for :product" would search for an instance variable of the same name and use the same route, product_path, but it seems I was mistaken. I've looked at other posts on this site, but they don't seem to mention the routes. Why I'm getting this error?
EDIT: I added the option "url: products_path" to the form_for :product line and it works now. I'm guessing the symbol doesn't know to use the routes in resources like #product does?
If you have a routing error, please check the file confg/routes.rb it must include resources :products. If not, add it and restart the server.
And yes at routes we use a symbol with the table name (plural of model) for define resources.
edited:
With form_for use an instance variable: #product that was initialized on controller with an empty new product, or the finded product to update.

Updating database Model view controller rails 4 method

I want to call a method that updates an attribute when saved.
Here is my index.html.erb
<% #users.each do |user| %>
<div>
<strong><%= user.email %></strong>
</div>
<% if can? :update, User %>
<%= link_to 'Make Author', User, method: :add_roles %>
<% end %>
Below is my user_controller.rb
def add_roles
respond_to do |format|
if #user.update(user_params)
user.author = true
user.save!
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
It doesn't update the attribute though. I think it is because I am not properly attaching it to the correct user. I want to current_user to update another user.
I think the solution is simple, first do what avlazarov said in his comment:
In the view <%= link_to 'Make Author', "/users/#{user.id}/add_roles",
method: :patch %> In the routes.rb patch '/users/:id/add_roles' =>
'user#add_roles'
Or something simpler, in routes.rb:
resources :users do
patch :add_roles, on: :member
end
then in view:
<%= link_to 'Make author', add_roles_users_path(user) %>
Then rename user_controller.rb placed in app/controllers to users_controller.rb and make sure the class name is UsersController.

Ruby on Rails update model with more than one row in a single form

I have 2 models Event and Tasks.
Event has many tasks. and task is a nested resource under event
so I first create a event and ask a user how many tasks it wants to create in it.
Let say I create a Event and a user wants to create 3 tasks in it. I want to do it in 2 steps and not one
After successful creation of event,now I go to /events/1/tasks/new
here I want to have 3 task name fields and when the user submits it, there should be 3 rows created in Task table against the Event 1
How do I achieve this
So here is the task _form.html.erb
<%= form_for [#event, #task] do |f| %>
<% if #task.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#task.errors.count, "error") %> prohibited this task from being saved:</h2>
<ul>
<% #task.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_field :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Task controller
def new
#event=Event.find(params[:event_id])
#event.task_count do
#choice = #event.tasks.build
end
respond_to do |format|
format.html # new.html.erb
format.json { render json: #task }
end
end
# POST /tasks
# POST /tasks.json
def create
#task = Task.new(params[:task])
respond_to do |format|
if #task.save
format.html { redirect_to #task, notice: 'Task was successfully created.' }
format.json { render json: #task, status: :created, location: #task }
else
format.html { render action: "new" }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
I think you are making this more complicated by involving the tasks controller. Controllers direct actions in the web application. But by your description you seem to be wanting to have 3 tasks auto created when the event is created (if I'm understanding you correctly). Other than entering the initial names this doesn't really involve the user.
Have them submit the names and when the events controller creates the event it should create the tasks there.
If your nested resource is more complicated it is a job for nested forms. You might benefit from this screencast:
http://railscasts.com/episodes/196-nested-model-form-part-1
First you intilized 3 task aginst one eveent in this way
def new
#event=Event.find(params[:event_id])
3.times{#event.tasks.build}
respond_to do |format|
format.html # new.html.erb
format.json { render json: #task }
end
end
Then it will be surely create 3 task against 1 event.or you can help from ryan rails cast for nested forms also

Rails creating more than one row from a single form

I don't know if I am doing this in a wrong way but here is it.
I have 2 models Event and Tasks.
Event has many tasks. and task is a nested resource under event
so I first create a event and ask a user how many tasks it wants to create in it.
Let say I create a Event and a user wants to create 3 tasks in it. I want to do it in 2 steps and not one
After successful creation of event,now I go to /events/1/tasks/new
here I want to have 3 task name fields and when the user submits it, there should be 3 rows created in Task table against the Event 1
How do I achieve this
So here is the task _form.html.erb
<%= form_for [#event, #task] do |f| %>
<% if #task.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#task.errors.count, "error") %> prohibited this task from being saved:</h2>
<ul>
<% #task.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_field :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Task controller
def new
#event=Event.find(params[:event_id])
#event.task_count do
#choice = #event.tasks.build
end
respond_to do |format|
format.html # new.html.erb
format.json { render json: #task }
end
end
# POST /tasks
# POST /tasks.json
def create
#task = Task.new(params[:task])
respond_to do |format|
if #task.save
format.html { redirect_to #task, notice: 'Task was successfully created.' }
format.json { render json: #task, status: :created, location: #task }
else
format.html { render action: "new" }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
You can try like this
def new
#event=Event.find(params[:event_id])
3.times {#event.tasks.build}
respond_to do |format|
format.html # new.html.erb
format.json { render json: #task }
end
end

Resources