I am following along with Michael Hartls tutorial and I am trying to add the destroy action in order to delete users as admin. However, the link is not displaying under users name. I am also using Devise, not sure if that matters. Any help is greatly appreciated. Thanks
Controller/users
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted."
redirect_to users_path
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
_users.html
<li>
<class= "round-image-50"><%= image_tag(current_user.avatar.url(:thumb)) %>
<%= link_to user.name, user %>
<% if current_user.admin? && !current_user?(user) %>
<%= link_to "delete", user, method: :delete, data: { confirm: "You sure?" } %>
<% end %>
</li>
Routes
devise_for :admins
devise_for :users
resources :posts
resources :users
get "users/show"
get 'feed', to: 'posts#index', as: :feed
get "about" => "pages#about"
root "pages#home"
match 'users/:id' => 'users#show', via: :get
Migration
class AddAdminToUsers < ActiveRecord::Migration
def self.up
add_column :users, :admin, :boolean, :default => false
end
def self.down
remove_column :users, :admin
end
end
I was able to figure it out. The below code needed to go on my user index page instead not the _user.html page. Thank you for your help #Pavan
<% if current_user.admin? && !current_user?(user) %>
<%= link_to "delete", user, method: :delete, data: { confirm: "You sure?" } %>
<% end %>
Related
I am creating a sign up page for users and accounts. Each account will be accessible to multiple users, but only 1 user will be the account owner.
# account.rb
class Account < ActiveRecord::Base
belongs_to :owner, class_name: "User", foreign_key: "owner_id"
accepts_nested_attributes_for :owner
end
# user.rb
class User < ActiveRecord::Base
has_secure_password
end
# accounts_controller.rb
class AccountsController < ApplicationController
def new
#account = Account.new
#account.build_owner
end
def create
#account = Account.new(account_params)
if #account.save
env["warden"].set_user(#account.owner, scope: :user)
env["warden"].set_user(#account, scope: :account)
flash[:success] = "Your account has been successfully created."
redirect_to root_url(subdomain: #account.subdomain)
else
flash[:error] = "Sorry, your account could not be created."
render :new
end
end
private
def account_params
params.require(:account).permit(:account_name, :subdomain,
{:owner => [:username, :password, :password_confirmation]})
end
end
I have also tried ":owner_attributes" as the key for the nested attributes hash.
The sign up page is pretty straightforward:
# /views/accounts/new.html.erb
<h2>Sign Up</h2>
<%= form_for(#account) do |account| %>
<%= account.error_messages %>
<p>
<%= account.label :account_name %><br>
<%= account.text_field :account_name %>
</p>
<p>
<%= account.label :subdomain %><br>
<%= account.text_field :subdomain %>
</p>
<%= account.fields_for #account.owner do |owner| %>
<p>
<%= owner.label :username %><br>
<%= owner.text_field :username %>
</p>
<p>
<%= owner.label :password %><br>
<%= owner.password_field :password %>
</p>
<p>
<%= owner.label :password_confirmation %><br>
<%= owner.password_field :password_confirmation %>
</p>
<% end %>
<%= account.submit %>
<% end %>
When running tests on this code, the server output indicates that there is an unpermitted parameter "user", which causes the account creation to fail:
Started GET "/sign_up" for 127.0.0.1 at 2015-07-10 14:41:26 +0000
Processing by AccountsController#new as HTML
Rendered accounts/new.html.erb within layouts/application (51.7ms)
Completed 200 OK in 127ms (Views: 57.7ms | ActiveRecord: 31.2ms)
Started POST "/accounts" for 127.0.0.1 at 2015-07-10 14:41:26 +0000
Processing by AccountsController#create as HTML
Parameters: {"utf8"=>"✓", "account"=>{"account_name"=>"Test Firm", "subdomain"=>"test", "user"=>{"username"=>"User1", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}, "commit"=>"Create Account"}
Unpermitted parameter: user
For informational purposes, here is the test code that is being used:
RSpec.feature "Accounts", type: :feature do
owner = FactoryGirl.attributes_for(:user)
scenario "creating an account" do
visit root_path
click_link "Sign Up"
fill_in "Account name", :with => "Test Firm"
fill_in "Subdomain", :with => "test"
fill_in "Username", :with => owner[:username]
fill_in "Password", :with => owner[:password]
fill_in "Password confirmation", :with => owner[:password_confirmation]
click_button "Create Account"
success_message = "Your account has been successfully created."
expect(page).to have_content(success_message)
expect(page).to have_content("Signed in as #{owner[:username].downcase}")
expect(page.current_url).to eq("http://test.example.com/")
end
end
I'm confused why a parameter of "user" is being sent (and rejected) since both the model and controller are using the "owner" name? Thanks for any insight.
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)
Ok, really stuck here. Have read through all documentation and SO but can't figure out what's going on. Simple case of allowing a User to update their profile by uploading a file using Carrierwave. My log keeps showing an
Unpermitted parameters: avatar
error.
My view:
<%= form_for current_user, html: { multipart: true } do |f| %>
<%= f.file_field :avatar %>
<%= f.submit "Update Profile", remote: true, class: "btn btn-default" %>
<% end %>
my controller:
class UsersController < ApplicationController
def update
current_user.update_attributes(user_params)
respond_to do |format|
format.html { render 'show'}
format.js {}
end
end
def user_params
params.fetch(:user, {}).permit(:id, :email, :username, :password, :password_hash, :avatar, projects_attributes: [:id, :user_id, :name])
end
end
my model
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
end
have not touched the uploader file
class AvatarUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
Seems pretty straightforward, why am I getting this error?
UPDATE: Server Log:
Started PATCH "/users/91" for 127.0.0.1 at 2014-04-03 11:38:22 -0700
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"+cLF9iPcZSL3Lc87o7/gPZjwgc9ySr7dOKmm6Yxhbno=", "user"=>{"username"=>"s+ Index", "avatar_attributes"=>#<ActionDispatch::Http::UploadedFile:0x007f81db4060c8 #tempfile=#<Tempfile:/var/folders/xl/wpdby5jn59q7hy9w0v2nv8xc0000gn/T/RackMultipart20140403-3890-127m17g>, #original_filename="cover.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"user[avatar_attributes]\"; filename=\"cover.jpg\"\r\nContent-Type: image/jpeg\r\n">}, "commit"=>"Update Profile", "id"=>"91"}
[1m[35mUser Load (0.4ms)[0m SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 91]]
Unpermitted parameters: avatar
Just had the same problem,
I had a problem because I had fallowed documentation like so:
<%= f.file_field :avatar, multiple: true %>
Changed it to:
<%= form_for #user, multiple: true do |f| %>
in controller:
params.require(:user).permit(:avatar,...)
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 to rails and am having challenges adding a commenting system to my listings model. Effectively I have listings that are created by users, and I want the ability to allow other users to make comments on these listings.
What I have so far:
A listing model that includes:
has_many :comments
a comment model that includes:
belongs_to :listing
a comments controller:
class CommentsController < ApplicationController
def create
#listing = Listing.find(params[:listing_id])
#comment = #listing.comments.build(params[:body]) # ***I suspected that I needed to pass :comment as the params, but this throws an error. I can only get it to pass with :body ***
respond_to do |format|
if #comment.save
format.html { redirect_to #listing, notice: "Comment was successfully created" }
format.json { render json: #listing, status: :created, location: #comment }
else
format.html { render action: "new" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
end
def comment_params
params.require(:comment).permit(:body, :listing_id)
end
and finally a listing view that includes the following code to collect and display comments:
<div class="form-group">
<%= form_for [#listing, Comment.new] do |f| %>
<%= f.label :comments %>
<%= f.text_area :body, :placeholder => "Tell us what you think", class: "form-control", :rows => "3" %>
<p><%= f.submit "Add comment", class: "btn btn-primary" %></p>
<% end %>
</div>
<%= simple_form_for [#listing, Comment.new] do |f| %>
<p>
<%= f.input :body, :label => "New comment", as: :text, input_html: { rows: "3" } %>
</p>
<p><%= f.submit "Add comment", class: "btn btn-primary" %></p>
<% end %>
The comment box is displaying correctly in the view, and I'm able to submit comments, however it appears that the :body isn't being saved, and therefore the "submitted x minutes ago" is the only thing that's showing up in my comments section.
Any ideas on what I could be doing incorrectly? I suspect it's a params issue, but haven't been able to work it out.
Thanks!
Since you are using the strong_parameters paradigm in Rails 4, I think you should change the comment creation line to this:
#comment = #listing.comments.build(comment_params)
And I'd change the listing finding line to this:
#listing = Listing.find(params.permit(:listing_id))
It should work fine, as long as you correctly whitelist all required params in the comment_params method.