Reputation: 576
I have added a "before_save" to my model to apply some logic to my model before saving. When I use this code, the record is created, then immediately updated (with the incorrect value). If I comment it out, there is no subsequent update when I create a new record.
Model
class Transaction < ApplicationRecord
belongs_to :account
attr_accessor :trx_type
before_save do
if self.trx_type == "debit"
self.amount = self.amount * -1
end
end
end
Controller
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
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.create(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)
end
def find_account
@account = current_user.accounts.find(params[:account_id])
end
def find_transaction
@transaction = @account.transactions.find(params[:id])
end
end
Console Output
Started POST "/accounts/1/transactions" for 127.0.0.1 at 2018-03-20 13:59:37 -0400
Processing by TransactionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"kURRN8FaHmjrDU7y5cikBLREGZdMgHm4PsVUcOHxn7MAlqmi2zolA0LYOKQ46JkTzXl+Fkgj1O6SlBhVjdM5Qw==", "transaction"=>{"trx_type"=>"debit", "trx_date(1i)"=>"2018", "trx_date(2i)"=>"3", "trx_date(3i)"=>"20", "description"=>"Test 10", "amount"=>"132"}, "commit"=>"Create Transaction", "account_id"=>"1"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Account Load (0.5ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."user_id" = $1 AND "accounts"."id" = $2 LIMIT $3 [["user_id", 1], ["id", 1], ["LIMIT", 1]]
(0.2ms) BEGIN
SQL (0.6ms) INSERT INTO "transactions" ("trx_date", "description", "amount", "account_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["trx_date", "2018-03-20"], ["description", "Test 10"], ["amount", "-132.0"], ["account_id", 1], ["created_at", "2018-03-20 13:59:37.349781"], ["updated_at", "2018-03-20 13:59:37.349781"]]
(3.5ms) COMMIT
(0.1ms) BEGIN
SQL (0.3ms) UPDATE "transactions" SET "amount" = $1, "updated_at" = $2 WHERE "transactions"."id" = $3 [["amount", "132.0"], ["updated_at", "2018-03-20 13:59:37.355748"], ["id", 27]]
(0.9ms) COMMIT
Redirected to http://localhost:3000/accounts/1/transactions/27
Completed 302 Found in 16ms (ActiveRecord: 6.6ms)
I'm new with Rails and trying to understand what is happening with my code. I appreciate any help in advance.
Thanks!
Upvotes: 3
Views: 699
Reputation: 11236
It seems like you need before_create
because it's unlikely you would change the type of transaction right?
before_create do
if self.trx_type == "debit"
self.amount = self.amount * -1
end
end
Update: Looks like you need in your controller change:
@transaction = @account.transactions.create(transaction_params)
to
@transaction = @account.transactions.build(transaction_params)
Upvotes: 1
Reputation: 337
There are two things here that are causing you some grief, but they're easy to address.
First, in the create action of your controller you're actually calling two methods that persist data to the database, so that's why you're seeing two saves in the console output.
The first line in the method is responsible for the first save:
@transaction = @account.transactions.create(transaction_params)
And this line here in your respond_to block is responsible for the second save:
if @transaction.save
Second, the reason the record has the correct amount
in the first save and not in the second save is related to the logic in the before_save
callback of your Transaction model. It's taking the amount
and calling * -1
on it. Since the first save has already made the amount negative, the second save will flip it back to positive.
Upvotes: 4