Ruby on Rails before_save在创建新记录后直接导致更新

时间:2021-10-10 01:20:59

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.

我在模型中添加了一个“before_save”,以便在保存之前对模型应用一些逻辑。当我使用此代码时,将创建记录,然后立即更新(使用不正确的值)。如果我注释掉它,当我创建一个新记录时,没有后续更新。

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.

我是Rails的新手,我试图理解我的代码发生了什么。我提前感谢任何帮助。

Thanks!

谢谢!

2 个解决方案

#1


3  

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)

@transaction = @account.transactions.create(transaction_params)

And this line here in your respond_to block is responsible for the second save:

respond_to block中的这一行负责第二次保存

if @transaction.save

如果@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.

其次,记录在第一个保存中有正确的数量,而在第二个保存中没有正确的数量的原因与事务模型的before_save回调中的逻辑有关。它取钱,并调用* -1。由于第一个储蓄已经使金额为负数,第二个储蓄将把它转回正数。

#2


1  

It seems like you need before_create because it's unlikely you would change the type of transaction right?

似乎需要before_create因为不太可能会改变事务类型,对吧?

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)

#1


3  

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)

@transaction = @account.transactions.create(transaction_params)

And this line here in your respond_to block is responsible for the second save:

respond_to block中的这一行负责第二次保存

if @transaction.save

如果@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.

其次,记录在第一个保存中有正确的数量,而在第二个保存中没有正确的数量的原因与事务模型的before_save回调中的逻辑有关。它取钱,并调用* -1。由于第一个储蓄已经使金额为负数,第二个储蓄将把它转回正数。

#2


1  

It seems like you need before_create because it's unlikely you would change the type of transaction right?

似乎需要before_create因为不太可能会改变事务类型,对吧?

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)