I'm using the carrierwave gem to upload images
I have this form in my users view which provides an upload button for a file (image)
<%= form_for(#user, html: { multipart: true }) do |f| %>
Update your profile pic:
<span class="avatar">
<%= f.file_field :avatar %>
</span>
<%= f.submit "Update", class: "btn btn-primary" %>
<% end %>
Here's my users controller
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:avatar)
end
end
When I click the submit button on the form from the browser, I get this http request come through.
Started PATCH "/users/1" for 127.0.0.1 at 2018-07-05 13:15:39 +0100
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"VXaEvnTD7nl+2d/0n1+iB/zwRX+Mf3nbMt0/Qr7m/nYpTmyXCxih981pIbGKGT9qdSfB7zyB6l
CKGdA9uiLouw==", "user"=>{"avatar"=>#<ActionDispatch::Http::UploadedFile:0x00007fbf2a794af0 #tempfile=#<Tempfile:/var/folders
/42/plkmf9zn755b0lvwc2_5k30c0000gn/T/RackMultipart20180705-37847-7s1tba.png>, #original_filename="Screen Shot 2018-07-03 at 1
0.23.17.png", #content_type="image/png", #headers="Content-Disposition: form-data; name=\"user[avatar]\"; filename=\"Screen S
hot 2018-07-03 at 10.23.17.png\"\r\nContent-Type: image/png\r\n">}, "commit"=>"Update", "id"=>"1"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], [
"LIMIT", 1]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
No template found for UsersController#update, rendering head :no_content
Completed 204 No Content in 161ms (ActiveRecord: 0.6ms)
But nothing is updated in the database (i.e. the avatar attribute for the current user still has a value of nil). I also can't see that the image has been saved anywhere.
It's your controller update method which is incomplete, for example :
def update
user = User.find(params[:id]) # Set user
if user.update(user_params) # Save to database
# Success, redirect to show/index/whatever
else
# Fail, render form again
end
end
And i recommend you to read this: http://guides.rubyonrails.org/action_controller_overview.html
Related
I have a really basic Rails 7 application at the moment. Using all the defaults.
For some reason, Rails is not entering/running the code within a controller I've defined.
Here is the related route.
scope module: 'admin', path: 'admin', as: 'admin' do
root 'home#index'
resources :users do
post :invite, on: :collection
end
end
Here is the related Controller method:
def invite
puts "INVITE" * 100
invite_params = params.require(:admin_invite).permit(:name, :email)
#invite = Admin::Invite.new(invite_params)
respond_to do |f|
if #invite.send_invite
f.html { redirect_to admin_users_url, notice: "Invite sent" }
else
f.html { redirect_to admin_users_url, status: :unprocessible_entity, error: "There was an error sending the invite" }
end
end
end
Here is the form that POSTs to that route.
= form_with model: #new_invite, url: invite_admin_users_url, html: {"data-turbo" => false} do |f|
= f.label :name
= f.text_field :name
= f.label :email
= f.email_field :email
= f.submit "Invite"
I thought maybe it had something to do with TURBO that comes with Rails 7, so I disabled it on this form, but it didn't resolve the issue.
When I click submit on that form, I get the following error: No template found.
Looking at the output in the terminal, it seems to be POSTing properly to the controller, but it's not running any of the code with the method. Even that puts statement I have at the beginning of the #invite method doesn't execute.
19:26:14 web.1 | Started POST "/admin/invite_user" for ::1 at 2022-09-07 19:26:14 -0400
19:26:14 web.1 | Processing by Admin::UsersController#invite as HTML
19:26:14 web.1 | Parameters: {"authenticity_token"=>"[FILTERED]", "admin_invite"=>{"name"=>"Test", "email"=>"test#test.com"}, "commit"=>"Invite"}
19:26:14 web.1 | User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
19:26:14 web.1 | No template found for Admin::UsersController#invite, rendering head :no_content
Any ideas?
When I submit a form with blank fields that are required I see the ROLLBACK and ActiveModel::Errors in the console but for some reason I can't render the errors in the view.
This is happening on both the new and edit templates.
Needless to say, creating a new record or editing an existing one with the required fields works fine.
This is what my code looks like:
Model: transactions.rb
class Transaction < ApplicationRecord
belongs_to :account
belongs_to :category
validates :date, :description, :amount, :category_id, :account_id, presence: true
end
Controller: transactions_controller.rb
def new
#transaction = Transaction.new
end
def create
#transaction = Transaction.create(transaction_params)
if #transaction.save
redirect_to account_path(#account), notice: "Transaction created"
else
render :new
end
end
def edit
if #transaction.amount > 0
#transaction_type = "income"
else
#transaction_type = "expense"
end
render :edit
end
def update
if #transaction.update(transaction_params)
redirect_to account_path(#account)
else
render :edit
end
end
private
def transaction_params
params.require(:transaction).permit(:date, :description, :amount, :category_id, :account_id)
end
View: new.html.erb (or edit.html.erb)
<ul>
<% #transaction.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
Log after submitting the form:
Started POST "/accounts/15/transactions" for 192.168.55.1 at 2019-09-07 03:00:24 +0000
Cannot render console from 192.168.55.1! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by TransactionsController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"CJd+/rB6LcUqQAz0ZvY0cf8Dcu+2qzUYLCQHLWE2GElZtJrKbKq2EODFeVvKG6NkE2MxtIIjRreqHAnKu2sJ9A==", "transaction"=>{"date(1i)"=>"2019", "date(2i)"=>"9", "date(3i)"=>"7", "description"=>"", "category_id"=>"1", "amount"=>"0"}, "transaction_type"=>"Expense", "commit"=>"Add Transaction", "account_id"=>"15"}
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ /var/lib/gems/2.6.0/gems/activerecord-5.2.3/lib/active_record/log_subscriber.rb:98
Account Load (0.1ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT $2 [["id", 15], ["LIMIT", 1]]
↳ app/controllers/transactions_controller.rb:64
(0.1ms) BEGIN
↳ app/controllers/transactions_controller.rb:24
Category Load (0.2ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/controllers/transactions_controller.rb:24
(0.2ms) ROLLBACK
↳ app/controllers/transactions_controller.rb:24
(0.1ms) BEGIN
↳ app/controllers/transactions_controller.rb:26
(0.1ms) ROLLBACK
↳ app/controllers/transactions_controller.rb:26
Rendering transactions/new.html.erb within layouts/application
If I add p #transaction.errors in the create method or go into the Rails console and run #transaction.errors I do get the errors:
irb(main):026:0> #transaction.errors
=> #<ActiveModel::Errors:0x0000000006447158 #base=#<Transaction id: nil, date: nil, description: nil, amount: nil, account_id: nil, created_at: nil, updated_at: nil, category_id:
nil>, #messages={:account=>["must exist", "can't be blank"], :category=>["must exist", "can't be blank"], :date=>["can't be blank"], :description=>["can't be blank"], :amount=>["can't be blank"]}, #details={:account=>[{:error=>:blank}, {:error=>:blank}], :category=>[{:error=>:blank}, {:error=>:blank}], :date=>[{:error=>:blank}], :description=>[{:error=>:blank}], :amount=>[{:error=>:blank}]}>
Something else I tried is adding these 2 lines to the new.html.erb:
<%= #transaction.errors %> renders: #<ActiveModel::Errors:0x00007ff650570500>
<%= #transaction.errors.full_messages %> renders: []
Processing by TransactionsController#create as JS, your form is being submitted using ajax, you need to render a js view or use a non-ajax request. Are you using form_with for your form? form_with defaults to remote forms, use form_for instead or update the view using a js response.
I have array selected_ids[] with items id from form, and see them in console when click on the button, but can't delete them in action.
My form:
<% for task in #tasks.where(active: true) %>
<li class="task">
<%= check_box_tag 'selected_ids[]', task.id, false, class: 'selectable' %>
<%= link_to task.title, task, class: "task-title text-dark" %>
</li>
<% end %>
</ul>
My action:
def delete_all
Task.where(id: params[:selected_ids]).destroy_all
#tasks = Task.where(user_id: current_user)
render "index"
end
My routes:
resources :tasks do
get :delete_all, on: :collection
end
Console, when click button:
Started DELETE "/tasks/delete_all" for 127.0.0.1 at 2019-01-18 10:44:18 +0200
Processing by TasksController#destroy as HTML
Parameters: {"utf8"=>"?", "authenticity_token"=>"3BlLqPDnC9IzaVqnCv6qO0KkKP7VNBU9yEnmm8eAKyb76f5eCEIYUq9Gxx4YNbtbcJo0AEi2c/ORs2E87sg0Aw==", "commit"=>"Delete selected", "selected_ids"=>["2", "1"], "id"=>"delete_all"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
? app/controllers/tasks_controller.rb:2
Task Load (0.3ms) SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = ? ORDER BY "tasks"."id" ASC LIMIT ? [["id", 0], ["LIMIT", 1]]
? app/controllers/tasks_controller.rb:101
Rendering public/404.html within layouts/application
Rendered public/404.html within layouts/application (0.4ms)
Filter chain halted as :set_task rendered or redirected
Completed 404 Not Found in 682ms (Views: 676.8ms | ActiveRecord: 0.6ms)
You have a before_action called set_task in your controller. This is code that will run before delete_all. If you don't want it to run for delete_all, add , except: [:delete_all] to the before_action line:
example:
# change this line somewhere near the top of your controller
before_action :set_task, except: [:delete_all]
If the before_action is set somewhere else like ApplicationController, you can add this line to TasksController instead instead for the same effect:
skip_before_action :set_task, only: [:delete_all]
I have made a github repo that you can find here just for this question. I have 3 models:
class User < ActiveRecord::Base
has_many :user_countries
has_many :event_countries,
-> { where(user_countries: {:event => true}) },
:through => :user_countries,
:source => :country
has_many :research_countries,
-> { where(user_countries: {:research => true}) },
:through => :user_countries
:source => :country
end
class UserCountry < ActiveRecord::Base
belongs_to :country
belongs_to :user
end
class Country < ActiveRecord::Base
# ...
end
So a user should be able to choose event_countries and research_countries.
here's my users controller (nothing complicated):
class UsersController < ApplicationController
respond_to :html, :json
before_action :get_user, only: [:show, :edit, :update]
before_action :get_users, only: [:index]
def index
end
def show
end
def edit
end
def update
#user.update_attributes(user_params)
respond_with #user
end
private
def get_user
#user = User.find(params[:id])
end
def get_users
#users = User.all
end
def user_params
params.require(:user).permit(:first_name, :event_countries => [:id, :name])
end
end
And here's my user show page:
<%= best_in_place #user, :first_name %>
<p> event countries: </p>
<%= best_in_place #user, :event_countries, place_holder: "click here to edit", as: :select, collection: Country.all.map {|i| i.name} %>
<%= link_to "users index", users_path %>
So there's really nothing complicated here. I can also succesfully edit my users first name, best_in_place is working fine.
The question is: how do I edit the event_countries ? As you can see I tried to use the collection option with the countries but when I try to select a country I get the following:
Processing by UsersController#update as JSON
Parameters: {"user"=>{"event_countries"=>"3"}, "authenticity_token"=>"l5L5lXFmJFQ9kI/4klMmb5jDhjmtQXwn6amj1uwjSuE=", "id"=>"6"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 6]]
(0.1ms) begin transaction
(0.1ms) rollback transaction
Completed 500 Internal Server Error in 3ms
NoMethodError (undefined method `each' for nil:NilClass):
app/controllers/users_controller.rb:17:in `update'
I don't understand what it's doing, I know it must be a problem with the collection option. If you need to see any file please check my repo here:
https://github.com/spawnge/best_in_place_join_models_twice . I have a spent a lot of time on this any answer/suggestion would be greatly appreciated :)
update:
I have tried this:
<%= best_in_place #user, :event_country_ids, as: :select, collection: Country.all.map { |i| i.name }, place_holder: "click here to edit", html_attrs: { multiple: true } %>
and I have added :event_country_ids to my user params:
params.require(:user).permit(:first_name, :event_country_ids)
And now I can see all the countries but when I select one here's what I get:
Started PUT "/users/3" for 127.0.0.1 at 2014-12-18 01:19:27 +0000
Processing by UsersController#update as JSON
Parameters: {"user"=>{"event_country_ids"=>"1"}, "authenticity_token"=>"aZAFIHgzdSL2tlFcGtyuu+XIJW3HX2fwQGHcB9+iYpI=", "id"=>"3"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 3]]
(0.0ms) begin transaction
Country Load (0.0ms) SELECT "countries".* FROM "countries" WHERE "countries"."id" = ? LIMIT 1 [["id", 1]]
Country Load (0.0ms) SELECT "countries".* FROM "countries" INNER JOIN "user_countries" ON "countries"."id" = "user_countries"."country_id" WHERE "user_countries"."event" = 't' AND "user_countries"."user_id" = ? [["user_id", 3]]
SQL (0.1ms) DELETE FROM "user_countries" WHERE "user_countries"."user_id" = ? AND "user_countries"."country_id" = 2 [["user_id", 3]]
SQL (0.0ms) INSERT INTO "user_countries" ("country_id", "event", "user_id") VALUES (?, ?, ?) [["country_id", 1], ["event", "t"], ["user_id", 3]]
(20.9ms) commit transaction
Completed 204 No Content in 28ms (ActiveRecord: 21.3ms)
As you can see it seems that it insert the right content: INSERT INTO "user_countries" ("country_id", "event", "user_id") VALUES (?, ?, ?) [["country_id", 1], ["event", "t"], ["user_id", 3]] However I get the Completed 204 No Content just after that. I don't understand when I refresh the page the input is empty. Any suggestion ?
Update 2:
I checked in the console and it works, I can add event_countries to a user. However it doesn't display the user's event_countries when I refresh the page, I guess that's because I'm using the event_country_ids object.
I think the following code should work:
<%= best_in_place #user, :event_country_ids, as: :select,
collection: Country.all.each_with_object({}) { |i, memo| memo[i.id] = i.name },
place_holder: "click here to edit",
html_attrs: { multiple: true } %>
Assuming you want the user to be able to assign multiple event_countries.
Reference
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many, specifically the collection_singular_ids= method created by has_many.
https://github.com/bernat/best_in_place#select, the structure of the collection needs to be a hash. For each key => value pair, the key is what's submitted with the form and the value is what's displayed to the user.
http://ruby-doc.org/core-2.1.5/Enumerable.html#method-i-each_with_object, each_with_object is a part of the core Ruby library.
I have public_activity working on my rails app for the guidelines model. But there is a problem with deleting a guideline. Updating and creating guidelines are fine.
The error says
ActiveRecord::RecordNotSaved (You cannot call create unless the parent is saved):
app/controllers/guidelines_controller.rb:228:in `destroy'
guidelines_controller.rb
def destroy
#guideline = Guideline.find(params[:id])
#guideline.destroy
#guideline.create_activity :destroy, owner: current_user
respond_to do |format|
format.html { redirect_to guidelines_url }
format.json { head :no_content }
end
end
def update
#guideline = Guideline.find(params[:id])
if #guideline.update_attributes(params[:guideline])
#guideline.create_activity :update, owner: current_user
end
def create
#guideline = current_user.guidelines.new(params[:guideline])
if #guideline.save
#guideline.create_activity :create, owner: current_user
end
guideline.rb
include PublicActivity::Common
view public_activity/guideline/_destroy.html.erb
deleted a guideline
<% if activity.trackable %>
<%= link_to activity.trackable.title, activity.trackable %>
<% else %>
which can no longer be viewed
<% end %>
the rails log says
Processing by GuidelinesController#destroy as HTML
Parameters: {"authenticity_token"=>"SpdYUFk0Bv1KuVg6oEuDUU4MI3eD6C1nV/3bmd5Xhsg=", "id"=>"9-jannit"}
Guideline Load (0.2ms) SELECT "guidelines".* FROM "guidelines" WHERE "guidelines"."id" = ? LIMIT 1 [["id", "9-jannit"]]
(0.1ms) begin transaction
Comment Load (0.3ms) SELECT "comments".* FROM "comments" WHERE "comments"."guideline_id" = 9
SQL (0.7ms) DELETE FROM "guidelines" WHERE "guidelines"."id" = ? [["id", 9]]
SOLR Request (224.9ms) [ path=#<RSolr::Client:0x007f9ebdc5ea50> parameters={data: <?xml version="1.0" encoding="UTF-8"?><delete><id>Guideline 9</id></delete>, headers: {"Content-Type"=>"text/xml"}, method: post, params: {:wt=>:ruby}, query: wt=ruby, path: update, uri: http://localhost:8982/solr/update?wt=ruby, open_timeout: , read_timeout: } ]
(3.3ms) commit transaction
(0.1ms) begin transaction
SOLR Request (4.8ms) [ path=#<RSolr::Client:0x007f9ebdc5ea50> parameters={data: <?xml version="1.0" encoding="UTF-8"?><delete><id>Guideline 9</id></delete>, headers: {"Content-Type"=>"text/xml"}, method: post, params: {:wt=>:ruby}, query: wt=ruby, path: update, uri: http://localhost:8982/solr/update?wt=ruby, open_timeout: , read_timeout: } ]
(0.1ms) commit transaction
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
Completed 422 Unprocessable Entity in 254ms
Perhaps you could try setting :autosave => true in your association. For instance:
class User < ActiveRecord::Base # or wherever location the guidelines association is being set
has_many :guidelines, :autosave => true
...
end