I'm getting ActiveModel::ForbiddenAttributesError in MicropostsController#create on line #2 in the create action.
Tried also changing f.hidden_field to hidden_field_tag but still getting ForbiddenAttributesError
micropost_controller
def create
tag = Tag.find(params[:micropost][:tag_id])
#micropost = tag.microposts.build(params[:micropost])
#micropost.user_id = current_user.id
if #micropost.save
flash[:success] = "Posted!"
redirect_to root_path
else
render 'static_pages/home'
end
end
tags_controller
def details
#tag = Tag.find(params[:id])
#microposts = #tag.microposts
#micropost = #tag.microposts.build if sign_in?
end
micropost form
<%= form_for(#micropost) do |f| %>
<%= render 'shared/error_messages', object: #micropost %>
<div class="field">
<%= f.text_area :content, placeholder: "Your post" %>
<%= f.hidden_field :tag_id %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
in tags.rb
has_many :microposts, dependent: :destroy
in microposts.rb
belongs_to :user
belongs_to :tag
It seems that you need to delete tag_id from your microposts parameters:
tag = Tag.find(params[:micropost].delete(:tag_id))
#micropost = tag.microposts.build(params[:micropost])
If that won't fix it, just whit-list the params (it's good idea anyway):
micropost_params = params.require(:micropost).permit(:name, :title, ...)
#micropost = tag.microposts.build(micropost_params)
this
def create
tag = Tag.find(params[:micropost][:tag_id])
#...
end
should probably be changed to this
def create
tag = Tag.find(params[:tag_id])
#...
end
Related
Trying to remove uploaded images using carrierwave
<%= f.fields_for :images do |ff| %>
<div class="form-group">
<label>
<%= ff.check_box :remove_image %>
<%= image_tag ff.object.image %>
</label>
</div>
<% end %>
Getting such params in controller
"images_attributes"=>{"0"=>{"remove_image"=>"0", "id"=>"13"}, "1"=>{"remove_image"=>"1", "id"=>"14"}, "2"=>{"remove_image"=>"0", "id"=>"15"}, "3"=>{"remove_image"=>"0", "id"=>"16"}, "4"=>{"remove_image"=>"0", "id"=>"17"}, "5"=>{"remove_image"=>"0", "id"=>"18"}}}
But when updating an object with these params nothing happens, what am I missing?
update
def update
#country = Country.find(params[:id])
if #country.update(country_params)
flash[:notice] = 'Country is successfully updated.'
redirect_to edit_admin_country_path
else
flash[:error] = #country.errors.full_messages[0]
render 'edit'
end
end
def country_params
permitted = [{images_attributes: ["image", "#original_filename", "#content_type", "#headers", "_destroy", "id", "remove_image"]}]
params.require(:country).permit(*permitted)
end
class Country < ActiveRecord::Base
has_many :images
....
end
class Image < ActiveRecord::Base
mount_uploader :image, ImageUploader
belongs_to :country
end
your form looks good but you are missing the controller action
mine looks like:
class ImageController < ApplicationController
...
def update
#image = Image.find(params[:id])
...
if params[:images][:remove_image].present?
#image.remove_image!
end
#image.save
end
end
If you want to remove the file manually, you can call remove_avatar!, then save the object.
Can anyone help me figure out why I am getting the following error?
When assigning attributes, you must pass a hash as an argument.
The form:
<%= form_for([current_user, current_user.suggestions.build]) do |f| %>
<%= f.label :category %>:
<%= f.select :category, ['Startup', 'Cars', 'Kids', 'Food', 'Other'] %> <br>
<%= f.text_area :suggestion %><br>
<%= f.submit 'Submit Suggestion' %>
<% end %>
SuggestionController:
def create
#suggestion = current_user.suggestions.create(suggestion_params)
end
private
def suggestion_params
params.require(:suggestion).permit(:suggestion, :category)
redirect_to shirts_path
end
ApplicationController:
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
suggestion_params is returning the last line of the method redirect_to shirts_path, which current_user.suggestions.create can't accept because it isn't acceptable data.
The redirect should be in create method:
def create
#suggestion = current_user.suggestions.create(suggestion_params)
redirect_to shirts_path
end
private
def suggestion_params
params.require(:suggestion).permit(:suggestion, :category)
end
Update: Zishe figured it out. Correct params.require code should be:
def adventure_params
params.require(:adventure).permit(:story, :choice, :parent_id, :user_id)
end
with parenthesis instead of a bracket, of course.
Original question:
I am making a form_for that should be submitting 4 attributes to the Adventure model. The adventure model has ancestry. I keep getting wrong number of arguments (4 for 1) based on my params.require method. When I change to requirements down to 1, I see that all the attributes are blank in my database. I know they are in the params but for some reason they are not being saved. Here is my code:
Form
<div class="form">
<%= form_for #adventure do |f| %>
<%= f.label :choice %>
<%= f.text_area :choice %>
<%= f.label :story %>
<%= f.text_area :story %>
<%= f.label :parent_id %>
<%= f.text_field :parent_id %>
<%= f.submit "Post"%>
<% end %>
</div>
Controller
class AdventuresController < ApplicationController
before_filter :authenticate_user!
def home
end
def index
end
def new
#parent_id = params[:parent_id]
#adventure = Adventure.new
end
def show
#adventure = Adventure.find(params[:id])
end
def create
#user = current_user
#adventure = current_user.adventures.build(adventure_params)
if #adventure.save
flash[:success] = "Adventure created!"
redirect_to #adventure
else
flash[:error] = "There was an error"
redirect_to adventures_path
end
end
private
def adventure_params
params.require(:adventure).permit[:story, :choice, :parent_id, :user_id]
end
end
Model
class Adventure < ActiveRecord::Base
has_ancestry
belongs_to :user
validates :user_id, presence: true
validates :parent_id, presence: true
end
I have no idea why I am getting wrong number of arguments since the attributes show up in the params.
Change permit[...] to:
.permit(:story, :choice, :parent_id, :user_id)
How can I upload multiple images from a file selection window using Rails 4 and CarrierWave? I have a post_controller and post_attachments model. How can I do this?
Can someone provide an example? Is there a simple approach to this?
This is solution to upload multiple images using carrierwave in rails 4 from scratch
Or you can find working demo :
Multiple Attachment Rails 4
To do just follow these steps.
rails new multiple_image_upload_carrierwave
In gem file
gem 'carrierwave'
bundle install
rails generate uploader Avatar
Create post scaffold
rails generate scaffold post title:string
Create post_attachment scaffold
rails generate scaffold post_attachment post_id:integer avatar:string
rake db:migrate
In post.rb
class Post < ActiveRecord::Base
has_many :post_attachments
accepts_nested_attributes_for :post_attachments
end
In post_attachment.rb
class PostAttachment < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
belongs_to :post
end
In post_controller.rb
def show
#post_attachments = #post.post_attachments.all
end
def new
#post = Post.new
#post_attachment = #post.post_attachments.build
end
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
params[:post_attachments]['avatar'].each do |a|
#post_attachment = #post.post_attachments.create!(:avatar => a)
end
format.html { redirect_to #post, notice: 'Post was successfully created.' }
else
format.html { render action: 'new' }
end
end
end
private
def post_params
params.require(:post).permit(:title, post_attachments_attributes: [:id, :post_id, :avatar])
end
In views/posts/_form.html.erb
<%= form_for(#post, :html => { :multipart => true }) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<%= f.fields_for :post_attachments do |p| %>
<div class="field">
<%= p.label :avatar %><br>
<%= p.file_field :avatar, :multiple => true, name: "post_attachments[avatar][]" %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
To edit an attachment and list of attachment for any post.
In views/posts/show.html.erb
<p id="notice"><%= notice %></p>
<p>
<strong>Title:</strong>
<%= #post.title %>
</p>
<% #post_attachments.each do |p| %>
<%= image_tag p.avatar_url %>
<%= link_to "Edit Attachment", edit_post_attachment_path(p) %>
<% end %>
<%= link_to 'Edit', edit_post_path(#post) %> |
<%= link_to 'Back', posts_path %>
Update form to edit an attachment views/post_attachments/_form.html.erb
<%= image_tag #post_attachment.avatar %>
<%= form_for(#post_attachment) do |f| %>
<div class="field">
<%= f.label :avatar %><br>
<%= f.file_field :avatar %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Modify update method in post_attachment_controller.rb
def update
respond_to do |format|
if #post_attachment.update(post_attachment_params)
format.html { redirect_to #post_attachment.post, notice: 'Post attachment was successfully updated.' }
end
end
end
In rails 3 no need to define strong parameters and as you can define attribute_accessible in both the model and accept_nested_attribute to post model because attribute accessible is deprecated in rails 4.
For edit an attachment we cant modify all the attachments at a time. so we will replace attachment one by one, or you can modify as per your rule, Here I just show you how to update any attachment.
If we take a look at CarrierWave's documentation, this is actually very easy now.
https://github.com/carrierwaveuploader/carrierwave/blob/master/README.md#multiple-file-uploads
I will use Product as the model I want to add the pictures, as an example.
Get the master branch Carrierwave and add it to your Gemfile:
gem 'carrierwave', github:'carrierwaveuploader/carrierwave'
Create a column in the intended model to host an array of images:
rails generate migration AddPicturesToProducts pictures:json
Run the migration
bundle exec rake db:migrate
Add pictures to model Product
app/models/product.rb
class Product < ActiveRecord::Base
validates :name, presence: true
mount_uploaders :pictures, PictureUploader
end
Add pictures to strong params in ProductsController
app/controllers/products_controller.rb
def product_params
params.require(:product).permit(:name, pictures: [])
end
Allow your form to accept multiple pictures
app/views/products/new.html.erb
# notice 'html: { multipart: true }'
<%= form_for #product, html: { multipart: true } do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
# notice 'multiple: true'
<%= f.label :pictures %>
<%= f.file_field :pictures, multiple: true, accept: "image/jpeg, image/jpg, image/gif, image/png" %>
<%= f.submit "Submit" %>
<% end %>
In your views, you can reference the images parsing the pictures array:
#product.pictures[1].url
If you choose several images from a folder, the order will be the exact order you are taking them from top to bottom.
Some minor additions to the SSR answer:
accepts_nested_attributes_for does not require you to change the parent object's controller. So if to correct
name: "post_attachments[avatar][]"
to
name: "post[post_attachments_attributes][][avatar]"
then all these controller changes like these become redundant:
params[:post_attachments]['avatar'].each do |a|
#post_attachment = #post.post_attachments.create!(:avatar => a)
end
Also you should add PostAttachment.new to the parent object form:
In views/posts/_form.html.erb
<%= f.fields_for :post_attachments, PostAttachment.new do |ff| %>
<div class="field">
<%= ff.label :avatar %><br>
<%= ff.file_field :avatar, :multiple => true, name: "post[post_attachments_attributes][][avatar]" %>
</div>
<% end %>
This would make redundant this change in the parent's controller:
#post_attachment = #post.post_attachments.build
For more info see Rails fields_for form not showing up, nested form
If you use Rails 5, then change Rails.application.config.active_record.belongs_to_required_by_default value from true to false (in config/initializers/new_framework_defaults.rb) due to a bug inside accepts_nested_attributes_for (otherwise accepts_nested_attributes_for won't generally work under Rails 5).
EDIT 1:
To add about destroy:
In models/post.rb
class Post < ApplicationRecord
...
accepts_nested_attributes_for :post_attachments, allow_destroy: true
end
In views/posts/_form.html.erb
<% f.object.post_attachments.each do |post_attachment| %>
<% if post_attachment.id %>
<%
post_attachments_delete_params =
{
post:
{
post_attachments_attributes: { id: post_attachment.id, _destroy: true }
}
}
%>
<%= link_to "Delete", post_path(f.object.id, post_attachments_delete_params), method: :patch, data: { confirm: 'Are you sure?' } %>
<br><br>
<% end %>
<% end %>
This way you simply do not need to have a child object's controller at all! I mean no any PostAttachmentsController is needed anymore. As for parent object's controller (PostController), you also almost don't change it - the only thing you change in there is the list of the whitelisted params (to include the child object-related params) like this:
def post_params
params.require(:post).permit(:title, :text,
post_attachments_attributes: ["avatar", "#original_filename", "#content_type", "#headers", "_destroy", "id"])
end
That's why the accepts_nested_attributes_for is so amazing.
Also I figured out how to update the multiple file upload and I also refactored it a bit. This code is mine but you get the drift.
def create
#motherboard = Motherboard.new(motherboard_params)
if #motherboard.save
save_attachments if params[:motherboard_attachments]
redirect_to #motherboard, notice: 'Motherboard was successfully created.'
else
render :new
end
end
def update
update_attachments if params[:motherboard_attachments]
if #motherboard.update(motherboard_params)
redirect_to #motherboard, notice: 'Motherboard was successfully updated.'
else
render :edit
end
end
private
def save_attachments
params[:motherboard_attachments]['photo'].each do |photo|
#motherboard_attachment = #motherboard.motherboard_attachments.create!(:photo => photo)
end
end
def update_attachments
#motherboard.motherboard_attachments.each(&:destroy) if #motherboard.motherboard_attachments.present?
params[:motherboard_attachments]['photo'].each do |photo|
#motherboard_attachment = #motherboard.motherboard_attachments.create!(:photo => photo)
end
end
Here is my second refactor into the model:
Move private methods to model.
Replace #motherboard with self.
Controller:
def create
#motherboard = Motherboard.new(motherboard_params)
if #motherboard.save
#motherboard.save_attachments(params) if params[:motherboard_attachments]
redirect_to #motherboard, notice: 'Motherboard was successfully created.'
else
render :new
end
end
def update
#motherboard.update_attachments(params) if params[:motherboard_attachments]
if #motherboard.update(motherboard_params)
redirect_to #motherboard, notice: 'Motherboard was successfully updated.'
else
render :edit
end
end
In motherboard model:
def save_attachments(params)
params[:motherboard_attachments]['photo'].each do |photo|
self.motherboard_attachments.create!(:photo => photo)
end
end
def update_attachments(params)
self.motherboard_attachments.each(&:destroy) if self.motherboard_attachments.present?
params[:motherboard_attachments]['photo'].each do |photo|
self.motherboard_attachments.create!(:photo => photo)
end
end
When using the association #post.post_attachments you do not need to set the post_id.
I'm new and I have a basic question
When I create a new user with devise using carmen-rails with Rails 4, country_code and states_code don't pass
Both country_code and state_code are permitted
Here are my codes:
in Devise registration:
= f.label :country_code
= f.country_select :country_code, {priority: %w(US CA), prompt: "Please select your country"}, :style => "width:170px;float:left;font-size:14px;", :type => 'text'
= f.label :state_code
= render partial: '/users/subregion_select', locals: {parent_region: f.object.country_code}
in users/ subregionselect:
<% parent_region ||= params[:parent_region] %> <% unless parent_region.nil? %>
<% country = Carmen::Country.coded(parent_region) %>
<% end %>
<% if country.nil? %>
Please select a country above
<% elsif country.subregions? %>
<%= f.subregion_select(:user, :state_code, parent_region) %>
<% else %>
<%= text_field(:user, :state_code) %>
<% end %>
in users.js.coffee
$('select#user_country_code').change (event) ->
select_wrapper = $('#user_state_code_wrapper')
$('select', select_wrapper).attr('disabled', true)
country_code = $(this).val()
url = "/users/subregion_options?parent_region=#{country_code}"
select_wrapper.load(url)
in routes.rb
get '/users/subregion_options' => 'users#subregion_options'
in users controllers:
def subregion_options
render partial: 'subregion_select'
end
On my console, all the data are saved except for country_code: nil (even though I put United States) and the states_code is not generated when I select a country.
Is that because I should need to use Formstatic?
Thank you for your help :)
Looks like an issue with Devise params sanitation.
#application_controller
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u|
u.permit(:email,
:password,
:password_confirmation,
:first_name,
:last_name,
:street1,
:street2,
:city,
:state,
:zipcode,
:country_code)
}
Give that a try!
Cheers