File_size validation with file_validators gem unpermitted parameter - ruby-on-rails

I added the file_validators gem to my app and called the validation in my vehicle_image.rb model that you can see below.
After attempting to upload a new image, I receive a unpermitted parameters message in the Rails console. I suspect the error has something to do with strong parameters? I attempted to assign the image prior to the if #vehicle.save but was unsuccessful.
edit:vehicle_image.rb
class VehicleImage < ActiveRecord::Base
belongs_to :vehicle
validates :image, file_size: { less_than_or_equal_to: 500.kilobytes, message: "Image must be less that 500kbs" }
mount_uploader :image, ImageUploader
def set_to_primary_and_save
VehicleImage.where(vehicle: vehicle).update_all(primary: false)
self.primary = true
save
end
end
stack trace
Started PATCH "/vehicles/65" for 127.0.0.1 at 2015-11-05 14:03:06 -0500
Processing by VehiclesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"o6W0JsKzxGe9D1z6VA2WeXW3b4JBVsfvYDvM4ANf4Eo5wVFBn1e31y+oKdLIsWFy41WXeW1BUenCzKTE6tni1Q==", "vehicle"=>{"make"=>"Pontiac", "model"=>"GTO", "year"=>"1967", "production_date"=>"January 5, 1968", "engine"=>"454 ", "transmission"=>"4 Speed Muncie", "trim"=>"Red", "color"=>"Black", "options"=>"Tinted Glass, Hurst Shifter", "location"=>"Milton, Ontario", "description"=>"sdfsdfdsf", "vehicle_images"=>{"image"=>[#<ActionDispatch::Http::UploadedFile:0x007f4adfa1c738 #tempfile=#<Tempfile:/tmp/RackMultipart20151105-7060-d0j694.jpg>, #original_filename="switzerland-3840x2160-alps-mountauns-stars-night-5713.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"vehicle[vehicle_images][image][]\"; filename=\"switzerland-3840x2160-alps-mountauns-stars-night-5713.jpg\"\r\nContent-Type: image/jpeg\r\n">], "image_cache"=>""}}, "commit"=>"Save", "id"=>"65"}
Vehicle Load (0.1ms) SELECT "vehicles".* FROM "vehicles" WHERE "vehicles"."id" = ? ORDER BY created_at DESC LIMIT 1 [["id", 65]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 6]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 6]]
Unpermitted parameter: vehicle_images
(0.0ms) begin transaction
(0.0ms) commit transaction
(0.1ms) begin transaction
SQL (0.3ms) INSERT INTO "vehicle_images" ("image", "vehicle_id", "primary", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["image", "switzerland-3840x2160-alps-mountauns-stars-night-5713.jpg"], ["vehicle_id", 65], ["primary", "t"], ["created_at", "2015-11-05 19:03:06.685379"], ["updated_at", "2015-11-05 19:03:06.685379"]]
(18.5ms) commit transaction
Redirected to http://localhost:3000/vehicles/65
Completed 302 Found in 2474ms (ActiveRecord: 19.4ms)
vehicles_controller.rb
class VehiclesController < ApplicationController
def index
scope = Vehicle.approved
scope = scope.filter_by_make(params[:makes]) if params[:makes].present?
scope = scope.filter_by_year(params[:years]) if params[:years].present?
#vehicles = scope
authorize #vehicles
end
def show
#vehicle = Vehicle.find(params[:id])
#primary_image, #images = #vehicle.primary_and_all_vehicle_images
end
def new
#vehicle = Vehicle.new
authorize #vehicle
end
def create
#vehicle = Vehicle.new(vehicle_params)
#vehicle.user = current_user
authorize #vehicle
if #vehicle.save
add_vehicle_images if params[:vehicle][:vehicle_images][:image]
create_registry_request(#vehicle)
flash[:notice] = "The Vehicle was sent to the Administrator for Approval. You will be notified in your Dashboard if your vehicle was approved or denied."
redirect_to current_user
else
flash[:error] = "There was an error saving the Vehicle to the Registry. Please try again."
render :new
end
end
def edit
#vehicle = Vehicle.find(params[:id])
authorize #vehicle
#primary_image, #images = #vehicle.primary_and_all_vehicle_images
end
def update
#vehicle = Vehicle.find(params[:id])
authorize #vehicle
if #vehicle.update_attributes(vehicle_params)
add_vehicle_images if params[:vehicle][:vehicle_images][:image]
flash[:notice] = "The Vehicle entry was updated."
redirect_to #vehicle
else
flash[:error] = "There was an error updating the Vehicle. Please try again."
#primary_image, #images = #vehicle.primary_and_all_vehicle_images
render :edit
end
end
def re_edit
#vehicle = Vehicle.find(params[:id])
authorize #vehicle
#primary_image, #images = #vehicle.primary_and_all_vehicle_images
end
def resubmit
#update and new request
#vehicle = Vehicle.find(params[:id])
authorize #vehicle
if #vehicle.update_attributes(vehicle_params)
add_vehicle_images if params[:vehicle][:vehicle_images][:image]
Vehicle.transaction do
#vehicle.active_registry_request.archive
create_registry_request(#vehicle)
end
flash[:notice] = "The Vehicle entry was updated and sent to the Administrator. Please wait for Approval."
redirect_to #vehicle
else
flash[:error] = "There was an error updating the Vehicle. Please try again."
#primary_image, #images = #vehicle.primary_and_all_vehicle_images
render :re_edit
end
end
private
def vehicle_params
params.require(:vehicle).permit(:make, :model, :year, :production_date, :engine, :transmission, :trim, :color, :options, :location, :description, vehicle_images_attributes: [:image])
end
def add_vehicle_images
params[:vehicle][:vehicle_images][:image].each_with_index do |img, i|
image = #vehicle.vehicle_images.build(image: img)
image.primary = true if i == 0
image.save!
end
end
def create_registry_request(vehicle)
RegistryRequest.create!(vehicle: vehicle)
end
end

Parameters: {
# ... snip ...
"vehicle" => { # ... snip ...
"vehicle_images"=>{ # ... snip ... }
}
}
But the parameter whitelist is specified as:
params.require(:vehicle).permit(..., vehicle_images_attributes: [:image])
"vehicle_images" is not equal to "vehicle_images_attributes", thus the message:
Unpermitted parameter: :vehicle_images
Either the form or the whitelist needs to change so that the key in the params hash matches the argument in permit.
Normally the _attributes suffix is added to the form when we use accepts_nested_attributes_for, but you don't appear to be doing that.

Related

In Rails, my model association with a view is causing a new SELECT statement for every row

I have an issue with bringing a view into my app, which calculates a running balance for a transactions table using SQL. I have the following models:
account.rb
class Account < ApplicationRecord
belongs_to :user
has_many :transactions, dependent: :destroy
validates :name, presence: true, length: { maximum: 50, minimum: 2 }
validates :starting_balance, presence: true
#validates_associated :transactions
after_create :create_initial_transaction
def create_initial_transaction
self.update_attributes(current_balance: 0.00)
Transaction.create(trx_type: 'credit', trx_date: DateTime.now, account_id: self.id, description: "Starting Balance", amount: self.starting_balance)
#self.update_attributes(current_balance: #initbalance)
end
end
transaction.rb
class Transaction < ApplicationRecord
belongs_to :account
has_one :transaction_balance
delegate :running_balance, to: :transaction_balance
attr_accessor :trx_type
#default_scope { order('trx_date, id DESC') }
validates_presence_of :trx_type, :message => "Please select debit or credit"
validates :trx_date, presence: true
validates :description, presence: true, length: { maximum: 150 }
validates :amount, presence: true, numericality: { greater_than_or_equal_to: 0 }
validates :memo, length: { maximum: 500 }
before_save :convert_amount
after_create :update_account_balance_new
after_update :update_account_balance_edit
after_destroy :update_account_balance_destroy
scope :with_balance, -> { joins(:transaction_balance) }
scope :desc, -> { order('trx_date, id DESC') }
# Determine the transaction_type for existing records based on amount
def transaction_type
if !new_record?
if self.amount >= 0
return ['Credit', 'credit']
else
return ['Debit', 'debit']
end
else
return ['Debit', 'debit']
end
end
private
def convert_amount
if self.trx_type == "debit"
self.amount = -self.amount.abs
end
end
def update_account_balance_new
#account = Account.find(account_id)
#account.update_attributes(current_balance: #account.current_balance + amount)
end
def update_account_balance_edit
#account = Account.find(account_id)
if saved_change_to_amount?
#account.update_attributes(current_balance: #account.current_balance - amount_was + amount)
end
end
def update_account_balance_destroy
#account = Account.find(account_id)
#account.update_attributes(current_balance: #account.current_balance - amount_was)
end
end
Basically my app allows users to create bank accounts, then add transactions to them to keep track of finances. I wanted to add a running balance at the transaction level, so I created a view which joins back to the transactions table 1:1 ....
Migration for view
class CreateTransactionBalancesView < ActiveRecord::Migration[5.1]
def up
execute <<-SQL
CREATE VIEW transaction_balances AS (
SELECT id AS transaction_id,
SUM(amount) OVER(PARTITION BY account_id ORDER BY trx_date, id) AS running_balance
FROM transactions
)
SQL
end
def down
execute("DROP VIEW transaction_balances")
end
end
Now, when I created a model for this view, I had issues referencing "belongs_to :transaction" because it was complaining that "transaction" was a reserved word, so I had to find a workaround, as seen in my model below:
transaction_balance.rb
class TransactionBalance < ApplicationRecord
self.primary_key = "transaction_id"
#belongs_to :transaction
belongs_to :user_transaction, foreign_key: "transaction_id", class_name: "Transaction"
end
transactions_controller.rb
class TransactionsController < ApplicationController
before_action :find_account
before_action :find_transaction, only: [:edit, :update, :show, :destroy]
# Index action to render all transactions
def index
#transactions = #account.transactions.paginate(page: params[:page], per_page: 25)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #transactions }
end
end
# New action for creating transaction
def new
#transaction = #account.transactions.build
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #transaction }
end
end
# Create action saves the trasaction into database
def create
#transaction = #account.transactions.build(transaction_params)
respond_to do |format|
if #transaction.save
format.html { redirect_to([#transaction.account, #transaction], :notice => 'Transaction was successfully created.') }
format.xml { render :xml => #transaction, :status => :created, :location => [#transaction.account, #transaction] }
else
format.html { render :action => "new" }
format.xml { render :xml => #transaction.errors, :status => :unprocessable_entity }
end
end
end
# Edit action retrieves the transaction and renders the edit page
def edit
end
# Update action updates the transaction with the new information
def update
respond_to do |format|
if #transaction.update_attributes(transaction_params)
format.html { redirect_to([#transaction.account, #transaction], :notice => 'Transaction was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #transaction.errors, :status => :unprocessable_entity }
end
end
end
# The show action renders the individual transaction after retrieving the the id
def show
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #transaction }
end
end
# The destroy action removes the transaction permanently from the database
def destroy
#transaction.destroy
respond_to do |format|
format.html { redirect_to(account_transactions_url) }
format.xml { head :ok }
end
end
private
def transaction_params
params.require(:transaction).permit(:trx_date, :description, :amount, :trx_type, :memo)
end
def find_account
#account = current_user.accounts.find(params[:account_id])
end
def find_transaction
#transaction = #account.transactions.find(params[:id])
end
end
And finally, my transactions index view, where I reference the running_balance field
<% #transactions.with_balance.desc.each do |transaction| %>
<tr class="row m-0">
<td class="col-sm-1 text-center"><%= link_to transaction.id, [transaction.account, transaction] %></td>
<td class="col-sm-1 text-center"><%= transaction.trx_date.strftime('%m/%d/%Y') %></td>
<td class="col-sm-4"><%= transaction.description %></td>
<td class="col-sm-2 text-right"><%= if transaction.amount >= 0 then number_to_currency(transaction.amount) end %></td>
<td class="col-sm-2 text-right"><%= if transaction.amount < 0 then "(" + number_to_currency(transaction.amount.abs) + ")" end %></td>
<td class="col-sm-2 text-right"><%= number_to_currency(transaction.running_balance) %></td>
</tr>
<% end %>
Now, my problem is when I access the transactions index page in browser, my server console shows the following:
Started GET "/accounts/1/transactions" for 127.0.0.1 at 2018-03-28 16:32:08 -0400
Processing by TransactionsController#index as HTML
Parameters: {"account_id"=>"1"}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Account Load (1.1ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."user_id" = $1 AND "accounts"."id" = $2 LIMIT $3 [["user_id", 1], ["id", 1], ["LIMIT", 1]]
Rendering transactions/index.html.erb within layouts/application
Transaction Load (0.8ms) SELECT "transactions".* FROM "transactions" INNER JOIN "transaction_balances" ON "transaction_balances"."transaction_id" = "transactions"."id" WHERE "transactions"."account_id" = $1 ORDER BY trx_date, id DESC LIMIT $2 OFFSET $3 [["account_id", 1], ["LIMIT", 25], ["OFFSET", 0]]
TransactionBalance Load (0.3ms) SELECT "transaction_balances".* FROM "transaction_balances" WHERE "transaction_balances"."transaction_id" = $1 LIMIT $2 [["transaction_id", 8], ["LIMIT", 1]]
TransactionBalance Load (1.5ms) SELECT "transaction_balances".* FROM "transaction_balances" WHERE "transaction_balances"."transaction_id" = $1 LIMIT $2 [["transaction_id", 7], ["LIMIT", 1]]
TransactionBalance Load (0.3ms) SELECT "transaction_balances".* FROM "transaction_balances" WHERE "transaction_balances"."transaction_id" = $1 LIMIT $2 [["transaction_id", 6], ["LIMIT", 1]]
TransactionBalance Load (0.2ms) SELECT "transaction_balances".* FROM "transaction_balances" WHERE "transaction_balances"."transaction_id" = $1 LIMIT $2 [["transaction_id", 5], ["LIMIT", 1]]
TransactionBalance Load (0.2ms) SELECT "transaction_balances".* FROM "transaction_balances" WHERE "transaction_balances"."transaction_id" = $1 LIMIT $2 [["transaction_id", 4], ["LIMIT", 1]]
TransactionBalance Load (0.3ms) SELECT "transaction_balances".* FROM "transaction_balances" WHERE "transaction_balances"."transaction_id" = $1 LIMIT $2 [["transaction_id", 3], ["LIMIT", 1]]
TransactionBalance Load (1.0ms) SELECT "transaction_balances".* FROM "transaction_balances" WHERE "transaction_balances"."transaction_id" = $1 LIMIT $2 [["transaction_id", 1], ["LIMIT", 1]]
(1.8ms) SELECT COUNT(*) FROM "transactions" WHERE "transactions"."account_id" = $1 [["account_id", 1]]
Rendered transactions/index.html.erb within layouts/application (59.8ms)
Account Load (0.5ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."user_id" = $1 [["user_id", 1]]
Rendered layouts/_navbar.html.erb (3.9ms)
Completed 200 OK in 255ms (Views: 169.1ms | ActiveRecord: 19.5ms)
As seen above, the view is being selected from for each individual record. What I expect it to do is to join the transactions table with the transaction_balances view in one single select statement. Any help on this would be greatly appreciated! Thanks!
In your TransactionsController#index action, change this line:
#transactions = #account.transactions.paginate(page: params[:page], per_page: 25)
to this (credit to #engineersmnky):
#transactions = #account.transactions.includes(:transaction_balance).references(:transaction_balance).paginate(page: params[:page], per_page: 25)
This will generate a single query that allows your view to access the transaction_balance for each transaction without going back to the database.
This is happening because you're not loading the TransactionBalance records when you load the transactions in your controller. Here's what you're doing:
#account.transactions
And here's what will fix the problem:
#account.transactions.with_balance
This will use the with_balance scope in your model, which does joins(:balance), which will load both the transacations and all their balances in the one query.
I'm not sure where you're getting your #account from in your TransactionsController, but you may want to do a join or an includes.
For instance, #account = Account.includes(:transactions).find(params[:account_id]) before you query the transactions will eager load the transactions and pull them in one query rather than "n+1-ing" them.
The API dock entry can tell you more about the includes method
and
This article can tell you more about how to get rid of the n+1 querying problem.
Good luck!

Error 404 when uploading a file caused by error in update function

I'm having a hard time trying to figure out what's wrong in my code. Basically, I'm using Dropzone-js's file uploader, that uses drag and drop. I can't upload a file because it returns me a 404 error.
I searched through my logs and found that I had a problem in my controller, in the update function.
The controller :
class ChatRoomsController < ApplicationController
before_action :authenticate_user!
before_action :set_room, only: [:index, :new, :create, :show, :edit, :signal]
before_action :set_participant, only: [:signal]
def index
#chat_rooms = ChatRoom.all
end
def show
# Show room messages
#chat_room = ChatRoom.includes(:messages).find_by(id: params[:id])
#message = Message.new
#chat_room.participant?(current_user)
# TODO: get participant only once
if params[:guid]
if #participant = User.find(params[:guid])
#participant.joined_at = Time.now
#chat_room.add_to_call(#participant)
end
elsif params[:del]
if #participant = User.find(params[:del])
#chat_room.remove_from_call(#participant)
end
end
response = {
room: #chat_room,
# Get all call participants
users: #chat_room.call_users,
signals: deliver_signals!
}
respond_to do |format|
format.html
format.json { render json: response }
end
end
def new
#chat_room = ChatRoom.new
end
def edit
# Empty
end
def create
#chat_room = current_user.chat_rooms.build(chat_room_params)
if #chat_room.save
#group_room.users << current_user
redirect_to chat_rooms_path
else
render 'new'
end
end
def update
#chat_room = ChatRoom.find(id: params[:chat_room_id])
if #chat_room.update_resource(chat_room_params)
flash[:success] = 'test'
else
render 'edit'
end
end
def signal
signal = signal_params
signal[:chat_room] = #chat_room
signal[:sender] = User.find(signal[:sender])
signal[:recipient] = #participant
logger.info('Signal is ' + signal.to_param)
ChatRoomSignal.create! signal
head 204
end
def deliver_signals!
data = ChatRoomSignal.where recipient: #participant
# Destroy the signals as we return them, since they have been delivered
result = []
data.each do |signal|
result << {
signal_type: signal.signal_type,
sender_guid: signal.sender_id,
recipient_guid: signal.recipient_id,
data: signal.data,
chat_room_id: signal.chat_room_id,
timestamp: signal.created_at
}
end
data.delete_all
result
end
private
def set_participant
#participant = User.find(params[:user_id])
rescue ActiveRecord::RecordNotFound
# Retry with ID as GUID
#participant = User.where(id: params[:user_id]).first
raise unless #participant
end
def set_room
#chat_room = ChatRoom.includes(:messages).find_by(id: params[:chat_room_id])
end
def chat_room_params
params.require(:chat_room).permit(:title, :image)
end
def signal_params
params.permit(:sender, :signal_type, :data)
end
HTML code for file upload :
<div class="panel-body">
<%= form_for #chat_room, html: { multipart: true, class: "dropzone", id: "my-dropzone"} do |f| %>
<div class="dz-message needsclick">
<h3>Drop a file here</h3> or <strong>click</strong> to upload
</div>
<div class="fallback">
<% f.file_field :image, as: :file %>
<%= f.submit "Upload your file" %>
</div>
<% end %>
</div>
The error :
[ActionCable] [test#test.com] ChatRoomsChannel is transmitting the subscription confirmation
[ActionCable] [test#test.com] ChatRoomsChannel is streaming from chat_rooms_1_channel
Started PATCH "/chat_rooms/1" for 127.0.0.1 at 2017-06-09 01:44:26 +0200
Processing by ChatRoomsController#update as JSON
Parameters: {"utf8"=>"✓", "authenticity_token"=>"p8KEWBx7fmJmEhHgINmp5rnj+PVwGXfbPHxslSaA4Z/5zA6HIJzxeBjwcz/+GcDEQKKwPwjXNJVnBtfq7xu2qw==", "chat_rooms"=>{"image"=># <ActionDispatch::Http::UploadedFile:0x007f640e58f5b0 #tempfile=#<Tempfile:/tmp/RackMultipart20170609-2887-1nuat54.png>, #original_filename="Screenshot from 2017-04-12 12-47-21.png", #content_type="image/png", #headers="Content-Disposition: form-data; name=\"chat_rooms[image]\"; filename=\"Screenshot from 2017-04-12 12-47-21.png\"\r\nContent-Type: image/png\r\n">}, "id"=>"1"}
[1m[36mUser Load (0.2ms)[0m [1m[34mSELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?[0m [["id", 1], ["LIMIT", 1]]
[1m[36mChatRoom Load (0.2ms)[0m [1m[34mSELECT "chat_rooms".* FROM "chat_rooms" WHERE "chat_rooms"."id" = ? LIMIT ?[0m [["id", nil], ["LIMIT", 1]]
Completed 404 Not Found in 7ms (ActiveRecord: 0.3ms)
ActiveRecord::RecordNotFound (Couldn't find ChatRoom with 'id'={:id=>nil}):
app/controllers/chat_rooms_controller.rb:62:in `update'
Line 62 :
#chat_room = ChatRoom.find(id: params[:chat_room_id])
So it seems like my controller is not able to find the id parameter, but i don't understand why. And this is probably the error that causes my file to return 404 error.
Thanks for the time you will take to read my post.
You are using the wrong key to get the id, try with params[:id]:
#chat_room = ChatRoom.find(params[:id])
Also notice that the id: has been removed, since find will look for the id provided as parameter.
Also, you should update your chat_room_params method:
def chat_room_params
params.require(:chat_rooms).permit(:title, :image)
end
Since you are updating only 2 attributes, you could refactor your update method like this:
def update
#chat_room = ChatRoom.find(id: params[:chat_room_id])
#chat_room.title = chat_room_params[:title]
#chat_room.image = chat_room_params[:image]
if #chat_room.save(chat_room_params)
flash[:success] = 'test'
else
render 'edit'
end
end
Your params[:chat_room_id] seems to be a hash..
Try
#chat_room = ChatRoom.find(id: params[:chat_room_id][:id])

Rails 5 Register nested attributes has_many through association with n+1

I have a problem with my small app build in Rails 5.
The project have only 4 tables: User, Custom, Contact, ContactCustom
The idea is an User register his customs fields and when he is going to add new contacts, the form should show the customs of the User logged in.
My problem is when I try to register a new contact with the customs of the user logged in, I have a n+1 inserting a nil register in ContactCustom table and don't catch the custom_id that I pass with a hidden_field.
My models are like this:
class Custom < ApplicationRecord
belongs_to :user
belongs_to :kind
has_many :contact_customs
has_many :contacts, through: :contact_customs
end
class ContactCustom < ApplicationRecord
belongs_to :contact, optional: true
belongs_to :custom, optional: true
accepts_nested_attributes_for :custom
end
class Contact < ApplicationRecord
belongs_to :user
has_many :contact_customs
has_many :customs, through: :contact_customs
accepts_nested_attributes_for :contact_customs
end
and here my contact_controller:
class ContactsController < ApplicationController
before_action :set_contact, only: [:show, :edit, :update, :destroy]
before_action :set_user_and_custom, only: [ :new, :create, :edit ]
def index
#contacts = Contact.all
end
def show
end
def new
#contact = Contact.new
#contact.contact_customs.build
end
def edit
end
def create
#contact = Contact.new(contact_params)
#contact.contact_customs.build
#binding pry
respond_to do |format|
if #contact.save
format.html { redirect_to #contact, notice: 'Contact was successfully created.' }
format.json { render :show, status: :created, location: #contact }
else
format.html { render :new }
format.json { render json: #contact.errors, status: :unprocessable_entity }
end
end
end
private
def set_contact
#contact = Contact.find(params[:id])
end
def set_user_and_custom
#user = current_user
#usercustom = Custom.where(user_id: #user)
end
def contact_params
params.require(:contact).permit(:email, :name, :user_id,
contact_customs_attributes: [ :id, :value, :custom_id, custom_attributes: [] ])
end
end
and here is my form ... I think that I made something wrong with the each loop:
<% #usercustom.each do |c| %>
<%= f.fields_for :contact_customs do |cc| %>
<div class="field">
<%= cc.label :value, c.name %>
<%= cc.text_field :value %>
</div>
<%= cc.fields_for :custom do |custom| %>
<%= custom.text_field :id, value: c.id %>
<% end %>
<% end %>
<% end %>
I don't know how to show how many custom fields as necessary without this loop and my query ~> #usercustom = Custom.where(user_id: #user) is registering one more nil record(n+1).
Here is the log message when I submit the contact form with only one custom record at Custom table:
Started POST "/contacts" for 127.0.0.1 at 2017-03-17 09:14:02 -0300
Processing by ContactsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"nIlwxH4Ua8DjAKkMpGh8B7nxwYf6gy1Fhkdh1PaMtSANx5sB6YaOKbBUekQ4M3KP56WuHgsX31iHq2lj4+fEwA==", "contact"=>{"email"=>"test#test", "name"=>"name test", "user_id"=>"1", "contact_customs_attributes"=>{"0"=>{"value"=>"custom test"}}}, "commit"=>"Create Contact"}
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.2ms) BEGIN
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
SQL (108.3ms) INSERT INTO "contacts" ("email", "name", "user_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["email", "test#test"], ["name", "name test"], ["user_id", 1], ["created_at", 2017-03-17 12:14:02 UTC], ["updated_at", 2017-03-17 12:14:02 UTC]]
SQL (7.7ms) INSERT INTO "contact_customs" ("value", "contact_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["value", "custom test"], ["contact_id", 3], ["created_at", 2017-03-17 12:14:02 UTC], ["updated_at", 2017-03-17 12:14:02 UTC]]
SQL (0.6ms) INSERT INTO "contact_customs" ("contact_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["contact_id", 3], ["created_at", 2017-03-17 12:14:02 UTC], ["updated_at", 2017-03-17 12:14:02 UTC]]
(36.5ms) COMMIT
Redirected to http://localhost:3000/contacts/3
Completed 302 Found in 176ms (ActiveRecord: 154.5ms)
Try to comment or remove #contact.contact_customs.build from you create action. That is the reason you have an N+1 contact_customs with all the fields = nil.
When you do #contact = Contact.new(contact_params) you create a #contact that should look like this:
#contact = Contact.new(:email => contact_params[:email], :name => contact_params[:name], ..etc..., :custom_attributes => contact_customs_attributes: [ :id, :value, :custom_id, custom_attributes: [] ])
With that you have a #contact object instantiated and as array:
#contact.contact_customs = [ first_contact_custom => [firstvalue, secondvalue], second_contact_custom => [firstvalue, secondvalue]]
The following:
#contact.contact_customs.build
is like doing
#custom = Custom.new()
#contact.customs << #custom
Which will append that #custom entry with all fields = nil in the join table contact_customs.
#contact.contact_customs = [ first_contact_custom => [firstvalue, secondvalue], second_contact_custom => [firstvalue, secondvalue], second_contact_custom => [nil, nil] ]
So try remove the following line
#contact.contact_customs.build
The only place where you need that line is in the new action, because you need those fields instantiated for the form.

Can't get user_id in database after Ajax call

In javascript I do an ajax call to the create function of deliveries_controller. This puts a new Delivery in the database with a product and quantity. I also try to put the current_user as user_id in the database, but for some reason it stays nil in the database.
My ajax call:
$.ajax({
type: "POST",
url: "/deliveries",
data: { delivery: {ingredient: "meel", quantity: "800", scenario_id: "3"} },
success: function(){
alert('Success!');
},
error: function(){
alert('No success');
}
});
I just pass some dummy data to test it all out.
and my deliveries_controller:
class DeliveriesController < ApplicationController
protect_from_forgery
def index
#storages = Storage.where(user_id: current_user)
end
def addQuantity
#storage = Storage.where(user_id: current_user.id)
#storage.update_all ("quantity = (quantity+200)")
redirect_to deliveries_url
end
def create
#delivery = Delivery.new(delivery_params)
respond_to do |format|
if #delivery.save
format.html do
render :nothing => true
end
format.json { render json: #delivery.to_json }
else
format.html { render :nothing => true} ## Specify the format in which you are rendering "new" page
format.json { render json: #delivery.errors } ## You might want to specify a json format as well
end
end
end
private
def delivery_params
params.require(:delivery).permit(:user_id, :ingredient, :quantity, :scenario_id)
end
end
New entries are created in the database, but whichever way I try to pass the user_id as param it isn't saved in the database.
I tried it like:
#delivery = Delivery.new(delivery_params, :user_id => current_user),
#user_id = current_user
#delivery = Delivery.new(delivery_params, #user_id)
and
params.require(:delivery).permit(:user_id, :ingredient, :quantity, :scenario_id).merge(user_id: current_user)
log:
Started POST "/deliveries" for 127.0.0.1 at 2014-11-03 12:59:37 +0100
Processing by DeliveriesController#create as */*
Parameters: {"delivery"=>{"ingredient"=>"meel", "quantity"=>"800", "scenario_id"=>"3"}}
Can't verify CSRF token authenticity
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' LIMIT 1
CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' LIMIT 1
(0.0ms) begin transaction
SQL (0.2ms) INSERT INTO "deliveries" ("created_at", "ingredient", "quantity", "scenario_id", "updated_at") VALUES (?, ?, ?, ?, ?) [["created_at", "2014-11-03 11:59:37.253274"], ["ingredient", "meel"], ["quantity", 800], ["scenario_id", 3], ["updated_at", "2014-11-03 11:59:37.253274"]]
(12.5ms) commit transaction
Rendered text template (0.0ms)
Completed 200 OK in 24ms (Views: 0.8ms | ActiveRecord: 13.1ms)
but the user_id for Delivery stays nil. How would I pass the user_id from the current_user so it's saved in the database with the json I retrieve from the ajax call?
Thanks in advance
EDIT:
I fixed it the following way:
I send json data to javascript with content_tag:
= content_tag(:div,"", id: "storages", data:{url: Storage.where(user_id: current_user)})
this data is handled, and the user_id is suddenly accepted :)
thanks for the help!
Try this instead
#delivery = current_user.deliveries.new(delivery_params)
It should be
#delivery = Delivery.new(delivery_params.merge(user: current_user))
OR
#delivery = Delivery.new(delivery_params)
#delivery.user = current_user
Put current user_id on hidden field on HTML and send it with ajax like other params

?? Rails 4.0 submiting range_field does not update params

I am thankful to be in the presence of experts
I am trying to use range_field sliders to update some user params/integers.
When I submit the form, the params update temporarily in a <%= #user.risk %> text field I have off to the side, but do not save to the database, when I reload the changes are gone. I am drawing heavily from Michael Hartl's Rails 4.0 Tutorial.
Below I initially used form_for(#user) but current_user seems to work better overall.
`
<%= form_for(current_user) do |f|%>
<%= f.range_field :risk, :in=>0..100, :id=>"slider1"%>
<%= f.range_field :tax, :in=>0..100, :id=>"slider2"%>
<%= f.range_field :income, :in=>0..100, :id=>"slider3"%>
<%= f.range_field :international, :in=>0..100,:id=>"slider4"%>
<%= f.submit "Save Profile" %>
<% end %>`
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
sign_in #user
flash[:success] ="Welcome"
redirect_to #user
else
render 'new'
end
end
def edit
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile Updated"
redirect_to #user
else render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation, :risk, :tax, :income, :international)
end
end
class User < ActiveRecord::Base
before_save :set_default
before_save { self.email = email.downcase }
before_create :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with:
VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.encrypt(token)
Digest::SHA1.hexdigest(token.to_s)
end
def set_default
self.risk = "50"
self.tax = "50"
self.income = "50"
self.international = "50"
end
private
def create_remember_token
self.remember_token = User.encrypt(User.new_remember_token)
end
end
And the output at the console, after I try to update params via range_field, and input User.find(4)
=> #<User id: 4, name: "Tony", email: "2#g.c", created_at: "2013-09-23 06:37:22",
updated_at: "2013-09-23 06:37:23", password_digest: "...", remember_token: "...",
risk: 50, tax: 50, income: 50, international: 50>
This is the output of my local WEBRick Rails Server
Started PATCH "/users/1" for 127.0.0.1 at 2013-09-23 06:15:32 -0700
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"...", "user"=>{"risk"=>"70", "tax"=>"61", "income"=>"54", "international"=>"58"}, "commit"=>"Save Profile", "id"=>"1"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", "1"]]
(0.0ms) begin transaction
User Exists (0.1ms) SELECT 1 AS one FROM "users" WHERE (LOWER("users"."email") = LOWER('2#g.c') AND "users"."id" != 1) LIMIT 1
(0.0ms) rollback transaction
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = '.....' LIMIT 1
(0.0ms) begin transaction
CACHE (0.0ms) SELECT 1 AS one FROM "users" WHERE (LOWER("users"."email") = LOWER('2#g.c') AND "users"."id" != 1) LIMIT 1
(0.0ms) rollback transaction
Rendered users/edit.html.erb within layouts/application (1.9ms)
Rendered layouts/_header.html.erb (0.2ms)
Completed 200 OK in 8ms (Views: 6.0ms | ActiveRecord: 0.3ms)
Thank you all very much
The solution was simple
def update
#user = User.find(params[:id])
#user.update_attributes!(user_params)
.....

Resources