Incorrect param submitting - ruby-on-rails

I have a form for casting your vote for your favourite image.
<%= form_for(#imagevote) do |f| %>
<% #miniature.collections(:photo).each do |collection| %>
<% if collection.photo.exists? %>
<td><div class="photo1">
<%= link_to image_tag(collection.photo.url(:thumb), :retina => true), collection.photo.url(:original), :retina => true, :class => "image-popup-no-margins" %>
<%= f.radio_button(:collection_id, collection.id) %>
<%= f.hidden_field :voter_id, :value => current_user.id %>
<%= f.hidden_field :voted_id, :value => collection.user_id %>
<%= f.hidden_field :miniature_id, :value => #miniature.id %>
<p>Painted by <%= link_to collection.user.name, collection.user %></p>
</div></td>
<% end %>
<% end %>
<%= f.submit "Vote" %>
<% end %>
Everything submits correctly except for the hidden_field :voted_id which for some reason duplicates the current_user.id.
UPDATE
I've tried logging in as another user and it seems that :voted_id is not duplicating current_user.id but rather that it is always "7" which was the :user_id I was using to test it before. Now logged in as user number 4 it is still entering the :voted_id as 7. I'm lost.
The link to the imagevotes view is as follows:
<%= link_to "See more and change your vote.", edit_imagevote_path(:miniature_id => #miniature, :voter_id => current_user.id) %>
Here is my image votes controller
class ImagevotesController < ApplicationController
respond_to :html, :js
def new
#imagevote = Imagevote.new
#miniature = Miniature.find(params[:miniature_id])
end
def edit
#imagevote = Imagevote.find_by_miniature_id_and_voter_id(params[:miniature_id],params[:voter_id])
#miniature = Miniature.find(params[:miniature_id])
end
def create
#imagevote = Imagevote.new(imagevote_params)
if #imagevote.save
flash[:success] = "Vote registered"
redirect_to :back
else
flash[:success] = "Vote not registered"
redirect_to :back
end
end
def update
#imagevote = Imagevote.find(params[:id])
if #imagevote.update_attributes(imagevote_params)
flash[:success] = "Vote changed."
redirect_to :back
else
redirect_to :back
end
end
private
def imagevote_params
params.require(:imagevote).permit(:collection_id, :voter_id, :voted_id, :miniature_id)
end
end

You only have one #imagevote object, but you are outputting the hidden fields inside your collection loop so you will have multiple fields in the form referencing the same attribute on the model: if you check the html that is generated, you should see multiple hidden fields with the same name attribute.
The way that browsers handle multiple inputs with the same name means that the param that comes through for :voted_id will always be the :user_id from the last collection.

It's difficult to say because you didn't provide your model and your loop code stripped.
I would guess that you loop over collection that belongs to the current_user. And in this case you will have current_user.id always be the same as collection.user_id. May be you wanted to see collection.photo_id?

Related

Why is my form not saving? form_for question

I have this form. I am new to rails and I am trying to write a simple ecommerce site. This is the only part not working. (It worked 2 days ago I sear)
<%= form_tag line_items_path do%>
<%binding.pry%>
<%= hidden_field_tag :lite_item, :order_id, #order.id%>
<%= hidden_field_tag :line_item, :menu_item_id, #menu_item.id%>
<%= number_field_tag :line_item, :quantity, 1 %>
<%= submit_tag "Add to Cart"%>
<% end %>
It gives params that look like:
#<ActionController::Parameters {"authenticity_token"=>"VECKnS5SBot1rCyekepPXZa7TyTYkfFi0KdNRTB617ZnelmQo8Lkz_cJmQ8nAmCHUdDlPu1mpkhrPvMKysfjew", "order_id"=>"1", "menu_item_id"=>"1", "quantity"=>"1", "commit"=>"Add to Cart", "controller"=>"line_items", "action"=>"create"} permitted: false>
The controller for the view looks like this:
class MenusController < ApplicationController
def index
#menu_items = MenuItem.all
end
def show
#menu_item = MenuItem.find(params[:id])
#line_items = current_order.line_items.build
end
end
The form is really going through the line_items controller
def create
binding.pry
#line_item = LineItem.create(line_item_params)
if #line_item.save
#order.line_item_id = #line_item.id
#order.save
redirect_to cart_path(#current_cart), notice: "Item added to cart."
else
redirect_to menu_path(#menu_item), alert: "Item did not add to cart."
end
end
With strong params like this
def line_item_params
params.require(:line_item).permit(:menu_item_id, :quantity, :order_id)
end
It should use the line_items_path POST>
If anything else is needed just ask. Thanks in advance.
There are a lot of problems here.
The signature is hidden_field_tag(name, value = nil, options = {}). So the parameters you would actually be creating with that form is:
{
"lite_item" => "order_id", # check your spelling...
"line_item" => "quantity"
}
Oops. And that not even going to happen as <%= number_field_tag :line_item, :quantity, 1 %> will raise since you're passing an integer where the method expects a hash.
If you really have to create the inputs manually you would want:
<%= hidden_field_tag "line_item[order_id]", #order.id %>
But since you actually have a model there is no reason why you should be using form_tag instead of form_for(#line_item) or form_with(model: #line_item).
<%= form_for(#line_item) do |form| %>
<%= form.hidden_field :order_id %>
<%= form.hidden_field :menu_item_id %>
<%= form.number_field :quantity %>
<%= form.submit_tag "Add to Cart"%>
<% end %>
The controller should also use the correct pluralization for the instance variable:
def show
#menu_item = MenuItem.find(params[:id])
#line_item = current_order.line_items.build
end
Your create method is also pretty questionable. All you should need is:
def create
# use .new not .create
#line_item = LineItem.new(line_item_params)
if #line_item.save
redirect_to cart_path(#current_cart), notice: "Item added to cart."
else
redirect_to menu_path(#menu_item), alert: "Item did not add to cart."
end
end
I have no idea why you think you need to update #order here. Your controller should just really be adding a row to what is essentially a join table.

How to redirect two buttons on a different path with the same action?

I try to create discount coupons subtracting total basket of my customers.
I'm not sure it's the best way to do it but I created a solution that works almost.
The only problem is that when I create coupon, I can't render the same page because my order's update method redirect to the checkout page. I want to different redirections, one for checkout when the customer click on the checkout button on my cart page, the other when the customer create a coupon on the same cart page. The two buttons uses the same update action.
Any idea how to solve it ?
Here's my orders_controller:
def update
#order = current_order
update_coupon
if #order.update(order_params)
redirect_to checkout_path
else
render 'new'
end
end
private
def update_coupon
if #order.update(:coupon => params[:order])
redirect_to cart_path
end
end
Here's my carts/show.html.erb:
<p>Total TTC: <%= number_to_currency #order.subtotal %></p>
<% if #order.add_reduc.nil? %>
<% else %>
<p style="color:green;">-<%= number_to_currency #order.add_reduc, id: "new_reduc" %></p>
#set coupon value to nil
<%= form_for #order do |f| %>
<%= f.hidden_field :coupon, :value => nil %>
<%= f.submit "x" %>
<% end %>
<p>Price after reduction: <%= number_to_currency #order.subtotal_with_reduc %></p>
<p style="color:green;"><%= #order.coupon_description %></p>
<% end %>
<%= form_for #order do |f| %>
<%= f.text_field :coupon, placeholder:'place your coupon' %>
<%= f.submit "Go coupon" %>
<% end %>
and my order.rb :
COUPONS = {
'MAREDUC' => '25% off',
'CHOCOLOVER' => '10€ free',
'PAPLAFUN' => '10% off'
}
def subtotal_all_inclusive
if self.add_reduc.nil?
subtotal + shipping
else
subtotal_with_reduc + shipping
end
end
def coupon_description
COUPONS[coupon]
end
def add_reduc
if self.coupon == "MAREDUC"
subtotal * 25 / 100
elsif self.coupon == "CHOCOLOVER" && self.subtotal >= 50
10
elsif self.coupon == "PAPLAFUN"
subtotal * 10 /100
else
nil
end
end
You may add a hidden field in the coupon form:
<%= form_for #order do |f| %>
<%= hidden_field_tag :from_coupon, 1 %>
<%= f.text_field :coupon, placeholder:'place your coupon' %>
<%= f.submit "Go coupon" %>
And then:
def update
#order = current_order
if params[:from_coupon]
update_coupon
else
if #order.update(order_params)
redirect_to checkout_path
else
render 'new'
end
end
end
Or you may create an additional action in routes.rb:
resources :orders do
member do
put 'update_coupon'
end
end
And then:
<%= form_for #order, :url => update_coupon_order_path(#order), :method => :put do |f| %>
And then remove private in controller.
Pass additional param to url and check if it present make different actions. Example
<%= form_for #order, url: order_path(#order, additional: "your_param") do |f| %>

Param is missing or the value is empty ROR

Good Day, i am trying to update the rating of a user using form_for to populate the text_field of its current rating and then update it to user entered value. So far, its seems all simple but something doesn't seem well and i am getting this error. Help will be highly appreciated!
user controller:
def edit_rating
#ad = Ad.find(params[:id])
#user = User.find(#ad.user_id)
if #user.update_attributes(user_params)
flash[:notice] = "User Updated Successfully"
redirect_to(:action => 'index')
else
end
end
def user_params
params.require(:user).permit(:firstName, :lastName, :email , :password , :location, :rating)
end
edit_rating view:
<h2>
Edit User Rating
</h2>
<h4>
Name: <%= #user.firstName %>
</h4>
<p>
Current Rating: <%= #user.rating%>
</p>
<%=form_for(:user , :url => {:action => 'edit_rating' , :id => #user.id}) do |f| %>
<p>
Updated Rating: <%= f.text_field(:rating) %>
</p>
<%= submit_tag("Update Rating") %>
<% end%>
This is the error that i am getting.
You can do the reverse not to permit you can only use the params you only want to use:
#user.update_attributes(rating: params[:user][:rating])

Multiple update forms for one model

I want to have multiple forms on one page. Let's make an example to understand what I want:
I have a page for my admins, let's say it's the admins#show page. My admin has to change his name on one form on this page and on another form his age. I know I could create one form but I want to have multiple forms (because this is just an example). So my admins#show page looks something like this:
<%= form_for #admin do |a| %>
<%= a.label :name %>
<%= a.text_field :name %>
<%= a.submit "Submit name change" %>
<% end %>
<%= form_for #admin do |e| %>
<%= e.label :age %>
<%= e.number_field :age %>
<%= e.submit "Submit age change" %>
<% end %>
But in my controller, I don't know really how this works and here is my problem. I think I have something like this, but how could I divide the form inputs in the update method?:
def edit
#admin = Admin.find(params[:id])
end
def update
#admin= Admin.find(params[:id])
if #admin.update_attributes(:name=> admin_params1[:name])
redirect_to #admin
else
render 'edit'
end
if #admin.update_attributes(:age=> admin_params2[:age])
redirect_to #admin
else
render 'edit'
end
end
private
def admin_params1
params.require(:admin).permit(:name)
end
def admin_params2
params.require(:admin).permit(:age)
end
Its a bit Unorthodox what you are doing, but as you insisted and only its an example, I guess you can handle the update method by doing like this
def update
#admin= Admin.find(params[:id])
if params[:commit] == "Submit name change"
if #admin.update_attributes(admin_params1)
redirect_to #admin
else
render 'edit'
end
elsif params[:commit] == "Submit age change"
if #admin.update_attributes(admin_params2)
redirect_to #admin
else
render 'edit'
end
end
end
Note: Not Tested!
Well, I think you could create other non-REST methods in the controller and then add named routes in your config/routes then add your two different forms similar to this;
<%= form_for :admin_name, url: admin_name_path, method: :post do |a| %>
<%= a.label :name %>
<%= a.text_field :name %>
<%= a.submit "Submit name change" %>
<% end %>
<%= form_for :admin_age, url: admin_age_path, method: :post do |e| %>
<%= e.label :age %>
<%= e.number_field :age %>
<%= e.submit "Submit age change" %>
<% end %>
Then something like this;
def update_age
#admin = Admin.find(params[:admin_age][:id])
if params[:admin_age]
#admin.update_attributes(:age=> params[:admin_age][:age])
redirect_to #admin
else
render 'edit'
end
end
def update_name
#admin = Admin.find(params[:admin_name][:id])
if params[:admin_name]
#admin.update_attributes(:name=> params[:admin_name][:name])
redirect_to #admin
else
render 'edit'
end
end
** not tested for bugs

Rails 3 & Devise: Keeping track of who-owns-profile for user posts

My app has a user model and a post model, where user has_many posts and posts belong_to users. Posts are displayed on a user's profile page. I'd like for any user to be able to post on his own, or any other user's profile page. However, the problem I'm having is that while I know who is posting (current_user), I don't know whose profile current_user is on. I need to know this in order to assign the new post to that user's posts. How do I extract user id information from the profile currently being viewed, so I know where to assign the new post?
My micropost controller looks like:
class MicropostsController < ApplicationController
before_filter :authenticate_user!
def create
#user_of_page = User.find_by_name(params[:id])
#micropost = #user_of_page.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to :back
else
redirect_to about_path
end
end
def destroy
end
end
But I'm getting a NoMethodError: undefined method `microposts' for nil:NilClass. I assume this is because I'm making some mistake with the creation of the user_of_page variable, but I don't know what that is!
SOLUTION
Thanks Sam. I took your advice and ended up doing it like this:
I added a column to my Micropost table called belongs_to_id.
I then passed the id of the user whose profile is being shown from the user show view to the micropost controller using a hidden field in the micropost form, like so:
<%= form_for #micropost do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<div class="field">
<%= f.label :content, "Why that mood?" %>
<%= f.text_area :content %>
</div>
<div class="field">
<%= f.hidden_field :author, :value => current_user.name %>
<%= f.hidden_field :belongs_to_id, :value => #user.id %>
<%= f.hidden_field :agree, :value => "0" %>
<%= f.hidden_field :disagree, :value => "0" %>
<%= f.hidden_field :amused, :value => "0" %>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<% end %>
I then used this id value to search for the user to assign the post to, in the micropost controller, like so:
class MicropostsController < ApplicationController
before_filter :authenticate_user!
def create
#user_of_page = User.find(params[:micropost][:belongs_to_id])
#micropost = #user_of_page.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Micropost created!"
redirect_to :back
else
redirect_to about_path
end
end
def destroy
end
end
Magic! Thanks again, you helped me to see it in the right way.
I would do it like this:
class profiles_controller < AC
...
def show
#profile = User.find(params[:id]).profile || current_user.profile
#post = Post.new
end
..
end
/profiles/show.html.erb
...
Name: <%= #profile.full_name %>
...
<%= form_for #post do |f| %>
<%= hidden_field_tag #profile.user %>
...
<% end %>
class microposts_controller < AC
def create
profile_user = User.find(params[:user_id]) # Owner of the profile current_user is on
..
end
end
Not tested. Hope this helps.

Resources