rails before_create 在渲染时导致 nil id 'show' 创建视图 post

rails before_create causing nil id when rendering 'show' view from create post

我有一个 rails 模型,belongs_to 一个父 class。当提交 'new' 视图表单时,对 #create 进行 post 以插入新模型。在 model.rb 中,我有一个 before_create 回调,用于设置字段的起始值。在模型控制器创建方法中,我创建记录并重定向到新记录的 'show' 视图。问题是传递给 url 的记录 ID 始终为零。 如果我注释掉 before_create 语句,那么它每次都有效。 我在这里缺少什么?另外,为字段设置起始值的最佳方法是什么?我在 db 模型迁移或验证中猜测,但 before_create 方法似乎适用于此功能

@service = @account.services.create
   (0.2ms)  SAVEPOINT active_record_1
   (0.2ms)  RELEASE SAVEPOINT active_record_1
=> #<Service id: nil, account_id: 31, date: nil, default_lawn_cost: nil, cost: nil, description: nil, created_at: "2015-05-02 16:48:39", updated_at: "2015-05-02 16:48:39", paid: false>
>> @customer.accounts.create
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.6ms)  INSERT INTO "accounts" ("customer_id", "created_at", "updated_at", "total", "active") VALUES (?, ?, ?, ?, ?)  [["customer_id", 14], ["created_at", "2015-05-02 16:48:50.524798"], ["updated_at", "2015-05-02 16:48:50.524798"], ["total", 0.0], ["active", "t"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<Account id: 34, customer_id: 14, default_address: nil, address: nil, city: nil, state: nil, zip: nil, balance: nil, lawn_cost: nil, created_at: "2015-05-02 16:48:50", updated_at: "2015-05-02 16:48:50", note: nil, active: true, total: #<BigDecimal:4d3a668,'0.0',9(36)>, tax: nil>


class ServicesController < ApplicationController

   def new
     setup
     @service = @account.services.build
   end

   def create
      @customer = Customer.find(params[:customer_id])
      @account = @customer.accounts.find(params[:account_id])
      @service = @account.services.create(service_params)
      if @service.valid?
         add_service_to_account(@account, @service)
             @account.save
             redirect_to customer_account_service_path(@customer, @account, @service)
          else
             render 'new'
          end
       end
    end

class Service < ActiveRecord::Base
   belongs_to :account

   before_create :unset_paid

   validates :cost, format: { allow_blank: true, with: /\A$?\d{1,4}(\.\d{0,2})?\Z/ }

   def unset_paid
      self.paid = false
   end
end

这是视图错误:

ActionController::UrlGenerationError in ServicesController#create
No route matches {:account_id=>"31", :action=>"show", :controller=>"services", :customer_id=>"14", :id=>nil} missing required keys: [:id]
Extracted source (around line #21):

第 21 行

redirect_to customer_account_service_path(@customer, @account, @service)

相关路线:

 new_customer_account_service GET    /customers/:customer_id/accounts/:account_id/services/new(.:format)      services#new
edit_customer_account_service GET    /customers/:customer_id/accounts/:account_id/services/:id/edit(.:format) services#edit
     customer_account_service GET    /customers/:customer_id/accounts/:account_id/services/:id(.:format)      services#show
                              PATCH  /customers/:customer_id/accounts/:account_id/services/:id(.:format)      services#update
                              PUT    /customers/:customer_id/accounts/:account_id/services/:id(.:format)      services#update
                              DELETE /customers/:customer_id/accounts/:account_id/services/:id(.:format)      services#destroy


class CreateServices < ActiveRecord::Migration
  def change
    create_table :services do |t|
      t.references :account, index: true, foreign_key: true
      t.date :date
      t.boolean :default_lawn_cost
      t.decimal :cost
      t.string :description

      t.timestamps null: false
    end
  end
end

这就是我不喜欢 ActiveRecord 回调的部分原因;他们太聪明了,会制造一些奇怪的情况,让你对一些没有明确原因的错误感到不安。

如果before_createbefore_save回调returnsfalse,它将取消即将执行的数据库操作。 请参阅 this guide page 了解其工作原理。我猜这就是在这种情况下让您感到困惑的原因,因为一旦您尝试创建记录,就会调用 unset_paid 记录并意外取消整个 save/create 操作。反过来,这意味着新的未创建记录没有 id,因此当您尝试重定向到记录的 URL 时,路径变为 /accounts/nil.

最近在 Rails 世界流传着这样的建议,即您应该始终默认使用 save!create!,而不是它们的 un-banged 版本,因为前者会引发如果操作失败则报错。如果您使用了 @account.save!,您会立即看到一个异常,将您指向问题的核心(即,在尝试保存记录时出现问题),而不是让您怀疑您的路线是否被施了魔法。

希望对您有所帮助!