在 Rails 中实例化对象时如何初始化属性?

问题描述 投票:0回答:4

Clients
有很多
Invoices
。发票有一个
number
属性,我想通过增加客户之前的发票编号来初始化该属性。

例如:

@client = Client.find(1)
@client.last_invoice_number
> 14
@invoice = @client.invoices.build
@invoice.number
> 15

我想将此功能添加到我的

Invoice
模型中,但我不知道如何做。这就是我想象的代码:

class Invoice < ActiveRecord::Base
  ...
  def initialize(attributes = {})
    client = Client.find(attributes[:client_id])
    attributes[:number] = client.last_invoice_number + 1
    client.update_attributes(:last_invoice_number => client.last_invoice_number + 1)
  end
end

但是,当我打电话给

attributes[:client_id]
时,@client.invoices.build
没有
设置。

发票的

client_id
如何以及何时初始化,何时可以使用它来初始化发票的
number
?我可以将此逻辑放入模型中,还是必须将其放入控制器中?

ruby-on-rails model attributes initialization dry
4个回答
7
投票

生成将

invoices_number
列添加到用户表的迁移。然后在
Invoice
模型中写下:

class Invoice < ActiveRecord::Base
  belongs_to :user, :counter_cache => true
  ...
end

创建发票后,这将自动为用户增加

invoices_count
属性。


6
投票

这个怎么样:

class Invoice < ActiveRecord::Base
  ...
  def initialize(attributes = {})
    super
    self.number = self.client.invoices.size + 1 unless self.client.nil?
  end
end

2
投票

根据上面 Jonathan R. Wallace 的评论,这里有一些关于

after_initialize
的有用讨论:

Rails:不要覆盖 ActiveRecord 对象上的初始化

ActiveRecord::Base并不总是使用new来创建对象,因此可能不会调用initialize。我想在 ActiveRecord::Base 子类上使用哈希来存储一些计算值,所以我天真地这样做了:

class User < ActiveRecord::Base
 def initialize(args = nil)
   super
   @my_cache = {}
 end
end

但是我很快就遇到了一些“当你没有想到的时候你有一个 nil 对象!”问题。一些调试器调查显示,如果该对象已存在于数据库中,则当我调用 find_or_create_ 时,未设置 @my_cache 变量。深入研究源代码发现,active_record/base.rb 中的 instantiate 方法使用 allocate 来创建类,而不是 new 。这意味着当从数据库创建对象时,初始化方法被巧妙地避开。解决方案是使用“after_initialize”回调:

class User < ActiveRecord::Base
 def after_initialize
   @my_cache = {}
 end
end

还要注意一点,当将参数传递到新方法或创建方法时,在设置参数后会调用 after_initialize 。因此,您不能依赖在调用重写的访问器之前完成的初始化。

来自 http://blog.dalethatcher.com/2008/03/rails-dont-override-initialize-on.html


1
投票

首先,你不需要使用属性集合,你可以直接做

self.client_id
。更好的是,只要您的
belongs_to :client
中有
Invoice
,您就可以执行
self.client.last_invoice_number
。最后,如果更新或创建失败,您几乎总是希望引发异常,因此习惯使用
update_attributes!
,这是更好的默认选择。 (如果您对这些要点有任何疑问,请询问,我将详细介绍)

现在这已经不成问题了,您在 ActiveRecord 中遇到了一些问题,初始化方法几乎从来都不是正确的选择。 AR 为您提供了一系列方法来连接您需要的生命周期的任何点。这些是

after_create
after_destroy
after_save
after_update
after_validation
after_validation_on_create
after_validation_on_update
before_create
before_destroy
before_save
before_update
before_validation
before_validation_on_create
before_validation_on_update

您可能想要的是挂钩 before_create。像这样的东西

def before_create
  self.number ||= self.client.last_invoice_number + 1 unless self.client
end

它将为您的客户访问数据库,获取最后一个发票编号,将其加一,并将其设置为新编号,但前提是您尚未设置编号(||=将分配,但仅当左侧为零),并且仅当您在保存之前设置了客户端(或 client_id)。

© www.soinside.com 2019 - 2024. All rights reserved.