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
?我可以将此逻辑放入模型中,还是必须将其放入控制器中?
生成将
invoices_number
列添加到用户表的迁移。然后在 Invoice
模型中写下:
class Invoice < ActiveRecord::Base
belongs_to :user, :counter_cache => true
...
end
创建发票后,这将自动为用户增加
invoices_count
属性。
这个怎么样:
class Invoice < ActiveRecord::Base
...
def initialize(attributes = {})
super
self.number = self.client.invoices.size + 1 unless self.client.nil?
end
end
根据上面 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
首先,你不需要使用属性集合,你可以直接做
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)。