How can I update another Model using a Callback? - ruby-on-rails

I'm working with a Model called Recover. Prior to creating the model I would like to save the boolean attribute, Combo.occupied = true using the Recover.combo_id attribute as a reference.
It appears my SQL is executing the query properly, but it is not saving this attribute. How can I save Combo.occupied = true?
recover.rb:
before_create :checkin
protected
def checkin
x = Combo.find_by_id(combo_id).occupied =
true
end
Rails Console:
Started POST "/recovers" for 127.0.0.1
at 2011-01-06 17:07:24 -0800
Processing by
RecoversController#create as HTML
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"o1Iu3Y9/rVBOZPoDUgVP/tRfQ8GxbdWC40DbPq9YxUE=",
"recover"=>{"combo_id"=>"4",
"email"=>"jz#marin.edu"},
"commit"=>"Create Recover"} Recover
Load (0.2ms) SELECT "recovers"."id"
FROM "recovers" WHERE
("recovers"."email" =
'justin.zollars#marin.edu') LIMIT 1
Recover Load (0.1ms) SELECT
"recovers"."id" FROM "recovers" WHERE
("recovers"."combo_id" = 4) LIMIT 1
Combo Load (0.5ms) SELECT "combos".*
FROM "combos" WHERE ("combos"."id" =
4) LIMIT 1 AREL (0.5ms) INSERT INTO
"recovers" ("locker_number", "email",
"requests", "created_at",
"updated_at", "combo_id") VALUES
(NULL, 'justin.zollars#marin.edu',
NULL, '2011-01-07 01:07:24.287072',
'2011-01-07 01:07:24.287072', 4)
Redirected to
http://localhost:3000/recovers/14
Completed 302 Found in 119ms
RecoversController#create
def create
#recover = Recover.new(params[:recover])
respond_to do |format|
if #recover.save
format.html { redirect_to(#recover, :notice =>
'Recover was successfully created.') }
format.xml { render :xml => #recover, :status => :created,
:location => #recover }
else
format.html { render :action => "new" }
format.xml { render :xml => #recover.errors, :status =>
:unprocessable_entity }
end
end
end

You need to call save for the new value to be written to the database:
def checkin
combo = Combo.find_by_id(combo_id)
combo.occupied = true
combo.save!
end
This is easier if you use update_attribute. Also, if you have a belongs_to relationship, you can dispense with the find:
belongs_to :combo
def checkin
if combo # true unless combo_id is nil
combo.update_attribute(:occupied,true)
end
end
Note that update_attribute bypasses validation. If you need to validate, use update_attributes(:occupied=>true) instead.

Related

ActiveRecord not updating dirty attribute on save / update. Create OK. Possible Inheritance issue

I have a class structure using single table inheritance as follows:
CashAccount > AssetAccount > FinancialAccount
Relavent Controller Code:
def create
#financial_account = FinancialAccount.new(financial_account_params)
respond_to do |format|
if #financial_account.save
format.html { redirect_to #financial_account, notice: 'Account was successfully created.' }
else
format.html { render :new }
end
end
end
def update
respond_to do |format|
if #financial_account.update(financial_account_params)
format.html { redirect_to #financial_account, notice: 'Account was successfully updated.' }
else
format.html { render :edit }
end
end
end
def set_financial_account
#financial_account = FinancialAccount.find_by_id(params[:id])
unless #financial_account
redirect_to root_path, :flash => { :alert => "That Account does not exist." }
return
end
end
def update_params
params.require(#financial_account.model_name.param_key)
.permit(:name, :type, :description)
end
def create_params
params.require(:financial_account)
.permit(:name, :type, :description)
end
Create works. Update does not. There are no errors. The saves return true but the value is never changed. Here are some puts to debug the state as the object is updated:
puts #financial_account.changed? //false
#financial_account.assign_attributes(financial_account_params)
puts #financial_account.changed? //true
#financial_account.description_will_change!
puts #financial_account.changed? //true
puts #financial_account.description //New Value 1
puts #financial_account.save //true
puts #financial_account.description //Old Value
puts #financial_account.update(:description => "New Value 2") //true
puts #financial_account.description //Old Value
#financial_account.description = "New Value 3"
puts #financial_account.description //New Value 3
puts #financial_account.save //true
puts #financial_account.description //Old Value
Description is a simple text attribute of the FinancialAccount Class:
create_table "financial_accounts", id: :uuid, default: "uuid_generate_v4()", force: :cascade do |t|
...
t.text "description"
...
end
partial to_yaml print out of #financial_account to view stored value form user:
delegate_hash:
name: !ruby/object:ActiveRecord::Attribute::FromUser
name: name
value_before_type_cast: Test Account
type: *1
value: Test Account
description: !ruby/object:ActiveRecord::Attribute::FromUser
name: description
value_before_type_cast: New Value 1
type: *1
value: New Value 1
The database is PostgreSQL
UPDATE - Output requested by DjezzzL
Note: This is a multi-tenant app so i need to set business id
2.2.4 :001 > Business.current_id = 1
=> 1
2.2.4 :002 > tmp = FinancialAccount.first
FinancialAccount Load (0.6ms) SELECT "financial_accounts".* FROM "financial_accounts" WHERE "financial_accounts"."business_id" = $1 ORDER BY "financial_accounts"."id" ASC LIMIT 1 [["business_id", 1]]
=> #<RetainedEarningsAccount id: "0a282a84-2561-4820-8f21-b8063c1c2604", type: "RetainedEarningsAccount", name: "Owner's Retained Earnings", created_at: "2017-05-02 05:20:22", updated_at: "2017-05-02 05:20:22", description: "Old Value", business_id: 1, update_balance_flag: false, reference_number: "11", balance_cents: 0>
2.2.4 :003 > tmp.description = 'new value'
=> "new value"
2.2.4 :004 > p tmp.save
(0.2ms) BEGIN
FinancialAccount Exists (1.0ms) SELECT 1 AS one FROM "financial_accounts" WHERE ("financial_accounts"."reference_number" = '11' AND "financial_accounts"."id" != '0a282a84-2561-4820-8f21-b8063c1c2604' AND "financial_accounts"."business_id" = 1) LIMIT 1
RetainedEarningsAccount Load (0.3ms) SELECT "financial_accounts".* FROM "financial_accounts" WHERE "financial_accounts"."type" IN ('RetainedEarningsAccount') AND "financial_accounts"."id" = $1 LIMIT 1 [["id", "0a282a84-2561-4820-8f21-b8063c1c2604"]]
(0.2ms) COMMIT
true
=> true
2.2.4 :005 > p tmp.description
"Old Value"
=> "Old Value"
2.2.4 :006 > p tmp.reload.description
RetainedEarningsAccount Load (2.1ms) SELECT "financial_accounts".* FROM "financial_accounts" WHERE "financial_accounts"."type" IN ('RetainedEarningsAccount') AND "financial_accounts"."id" = $1 LIMIT 1 [["id", "0a282a84-2561-4820-8f21-b8063c1c2604"]]
"Old Value"
=> "Old Value"
Note: my attributes are being marked as dirty so not a duplicate of
ActiveRecord not saving after updating attribute
def create
#financial_account = FinancialAccount.new(create_params)
respond_to do |format|
if #financial_account.save
format.html { redirect_to #financial_account, notice: 'Account was successfully created.' }
else
format.html { render :new }
end
end
end
def update
respond_to do |format|
if #financial_account.update(update_params)
format.html { redirect_to #financial_account, notice: 'Account was successfully updated.' }
else
format.html { render :edit }
end
end
end
private
def create_params
params.require(#financial_account.model_name.param_key)
.permit(:name, :type, :description)
end
def update_params
params.require(:financial_account)
.permit(:name, :type, :description)
end
Use ActiveModel::Naming#param_key to get the param key for a polymorphic model. Also in this case its better to use two separate methods for whitelisting as it reduces the amount of code paths.

Couldn't update nested attributes in Rails 4.2.6

I migrated my Rails from 3.2 to Rails 4.2.6. I am having 2 tables where report :has_many => icons. I added strong parameters for report and icon_attributes. The create functionality is working fine and when coming to update functionality, I am able to update reports but couldn't update icons, instead new icon is created every time it hits update action.
This is my code:
report.rb:
class Report < ActiveRecord::Base
has_many :icons, -> { order 'position_id ASC'}
accepts_nested_attributes_for :icons, :reject_if => lambda { |a| a[:icon].blank? }, :allow_destroy => true
end
icon.rb:
class Icon < ActiveRecord::Base
belongs_to :report
end
reports_controller:
def update
respond_to do |format|
if #report.update_attributes(report_params)
#report.save
format.html { redirect_to(user_reports_url, :notice => 'Report was successfully updated.') }
format.json { render :json => { :success => true, :report_id => #report.id, :report_title => #report.title, :icon_array => #report.icons, :redirect => report_url(#report.id) } }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #report.errors, :status => :unprocessable_entity }
end
end
end
private
def report_params
params.require(:report).permit(:title, :comments, :remarks,{:icons_attributes => [:id, :icon, :rotation, :top, :_destroy]})
end
I have seen the log by putting puts in the controller, the icons are inserting at #report.update_attributes(report_params) step and this is the log:
Processing by ReportsController#update as JSON Parameters:
{"utf8"=>"✓", "report"=>{"title"=>"title1", "comments"=>"This is a comment",
"icons_attributes"=>{"0"=>{"id"=>"", "icon"=>"market_indicator",
"rotation"=>"0", "top"=>"", "_destroy"=>"false"}, "id"=>"87"}
Report Load (0.3ms) SELECT "reports".* FROM "reports" WHERE
"reports"."deleted_at" IS NULL AND "reports"."id" = ? LIMIT 1 [["id",
87]]
SQL (1.6ms) INSERT INTO "icons" ("icon", "rotation", "top")
VALUES (?, ?, ?) [["icon", "market"], ["rotation", "0"], ["top", ""],
["left", ""]] (12.0ms) commit transaction
ActiveRecord::Associations::CollectionProxy
I have put log as:
def update
puts #report.icons.inspect
respond_to do |format|
.....
end
it resulted as:
Icon Load (0.9ms) SELECT "icons".* FROM "icons" WHERE "icons"."report_id" = ? ORDER BY position_id ASC [["report_id", 91]]
<ActiveRecord::Associations::CollectionProxy [#<Icon id: 204, report_id: 91, icon: "asking_price", rotation: "", top: "150", left: "165">]>
Your "icon_attributes" is not passing the id of the icon along.
"icons_attributes"=>{"0"=>{"id"=>"", "icon"=>"market_indicator", "rotation"=>"0", "top"=>"", "_destroy"=>"false"}, "id"=>"87"}
You'll notice the id is blank. Since the id is blank rails thinks it is a new record and thus creates a new icon. The error lies in how you have made your 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

Parsing JSON Array Object in ROR

I am trying to parse a JSON Object to insert values in table(MySQL). JSON is recived at server but it is unable to read the values to be inserted. Its inserting NULL values.
Below is snapshot from my rails console.
Started POST "/lists.json" for 192.168.1.9 at 2013-08-13 11:38:46 +0530
Processing by ListsController#create as JSON
Parameters: {"list"=>[{"amount"=>"120", "status"=>"done", "itemno"=>"w01", "na
me"=>"t01"}]}
WARNING: Can't verify CSRF token authenticity
(1.0ms) BEGIN
SQL (1.0ms) INSERT INTO `lists` (`amount`, `itemno`, `name`, `status`) VALUES
(NULL, NULL, NULL, NULL)
(103.0ms) COMMIT
The Create method in my lists_controller.rb is as below
def create
lists = params[:list].collect{|key,list_attributes| List.new(list_attributes)}
all_list_valid = true
lists.each_with_index do |list,index|
unless list.valid?
all_list_valid = false
invalid_list = lists[index]
end
end
if all_list_valid
#lists = []
lists.each do |list|
list.save
#lists << list
end
format.html { redirect_to #list, notice: 'List was successfully created.' }
format.json { render json: #list, status: :created, location: #list }
else
format.html { render action: "new" }
format.json { render json: #list.errors, status: :unprocessable_entity }
end
end
I am not sure why it is taking NULL values even though "Parameters" seems to have correct values. Please advise . Thanks.
See documentation for the collect method.
irb(main):008:0> parameters = {"list"=>[{"amount"=>"120", "status"=>"done", "itemno"=>"w01", "name"=>"t01"}]}
=> {"list"=>[{"status"=>"done", "amount"=>"120", "itemno"=>"w01", "name"=>"t01"}]}
irb(main):009:0> parameters["list"]
=> [{"status"=>"done", "amount"=>"120", "itemno"=>"w01", "name"=>"t01"}]
irb(main):010:0> parameters["list"].collect{|list| p list}
{"status"=>"done", "amount"=>"120", "itemno"=>"w01", "name"=>"t01"}
=> [nil]

.save puts NULL in id field in Rails

Here's the model file:
class ProfileTag < ActiveRecord::Base
def self.create_or_update(options = {})
id = options.delete(:id)
record = find_by_id(id) || new
record.id = id
record.attributes = options
puts "record.profile_id is"
puts record.profile_id
record.save!
record
end
end
This gives me the correct print out in my log. But it also says that there's a call to UPDATE that sets profile_id to NULL. Here's some of the output in the log file:
Processing ProfilesController#update (for 127.0.0.1 at 2010-05-28 18:20:54) [PUT]
Parameters: {"commit"=>"Save", "profile"=>{"id"=>"2", "password_confirmation"=>"", "username"=>"user2", "first_name"=>"user2_first", "password"=>"", "last_name"=>"user2_last"}, "authenticity_token"=>"...", "tag"=>"1", "id"=>"2"}
?[4;36;1mProfileTag Create (0.0ms)?[0m ?[0;1mINSERT INTO `profile_tags`
(`reputation_value`, `updated_at`, `tag_id`, `id`, `profile_id`, `created_at`) VALUES(0, '2010-05-29 01:20:54', 1, NULL, 4, '2010-05-29 01:20:54')?[0m
?[4;35;1mSQL (2.0ms)?[0m ?[0mCOMMIT?[0m
?[4;36;1mSQL (0.0ms)?[0m ?[0;1mBEGIN?[0m
?[4;35;1mSQL (0.0ms)?[0m ?[0mCOMMIT?[0m
?[4;36;1mProfileTag Load (0.0ms)?[0m ?[0;1mSELECT * FROM `profile_tags` WHERE (`profile_tags`.profile_id = 4) ?[0m
?[4;35;1mSQL (1.0ms)?[0m ?[0mBEGIN?[0m
?[4;36;1mProfileTag Update (0.0ms)?[0m ?[0;1mUPDATE `profile_tags` SET profile_id = NULL WHERE (profile_id = 4 AND id IN (35)) ?[0m
I'm not sure I understand why the INSERT puts the value into profile_id properly, but then it sets it to NULL on an UPDATE.
[Edit]
In ProfileController:
def update
#...stuff. Set tags array.
save_tags(tags) #These tags are correct. Verified by printouts before and after this call.
respond_to do |format|
if #profile.update_attributes(params[:profile])
flash[:notice] = 'Profile was successfully updated.'
#format.html { redirect_to(#profile) }
format.html { redirect_to :action=>'show' }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #profile.errors, :status => :unprocessable_entity }
end
end
end
def save_tags(tags)
profile = find_profile #finds the correct profile. And I confirm that it exists with a printout
tags.each do |t|
ProfileTags.create_or_update(:profile_id => profile.profile_id, :tag_id => t.id)
end
end
If you need more specifics, please let me know. I'm thinking that the save functionality does many things other than INSERTs into the database, but I don't know what I need to specify so that it will properly set profile_id.
Look at the line:
ProfileTags.create_or_update(:profile_id => profile.profile_id, :tag_id => t.id)
I believe you want to pass profile.id, and not profile.profile_id (which is probably null).
save! itself should't do this.
Maybe your problem is the name of the method. ActiveRecord::Base already have a method named create_or_update (see http://github.com/rails/rails/blob/2-3-stable/activerecord/lib/active_record/base.rb#L2913) which is called by save! - maybe replacing it causes this weird problem.
Try changing the name of your method to something else, it might help.
You aren't passing the id attribute to the create_or_update method in the first place, so you don't need to call it, just call create instead, like so:
def save_tags(tags)
profile = find_profile #finds the correct profile. And I confirm that it exists with a printout
tags.each do |t|
ProfileTag.create(:profile_id => profile.profile_id, :tag_id => t.id)
end
end

Resources