【问题标题】:How do I initialize attributes when I instantiate objects in Rails?在 Rails 中实例化对象时如何初始化属性?
【发布时间】:2010-06-17 04:04:13
【问题描述】:

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

但是,当我调用@client.invoices.build 时,attributes[:client_id] 没有设置。

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

【问题讨论】:

  • 我不关注。您想要最后一张发票的编号(如序列号)还是希望 last_invoice_number 反映用户拥有多少张发票?
  • last_invoice_number 是每个客户的序列号。因此,如果我刚刚向客户 = 4 发送了一张发票,并且它的编号为 42,那么我发送给该客户的下一张发票的编号为 43。

标签: ruby-on-rails model attributes initialization dry


【解决方案1】:

生成将@​​987654321@ 列添加到用户表的迁移。然后在Invoice 模型中这样写:

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

这将在创建发票后自动为用户增加invoices_count 属性。

【讨论】:

  • 哇,我不知道你能做到这一点!这非常有用。
【解决方案2】:

这个怎么样:

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

【讨论】:

  • 啊...捂脸时刻。 Eimantas 拿了蛋糕,但这对我还是很有用的!
  • 我应该补充一点,当我调用 @client.invoices.build 时,此方法似乎不起作用 - 它仅适用于 Invoice.new(:client_id =&gt; @client.id)。不知道为什么:S
  • 我很想知道为什么在通过关系构建对象实例时它不起作用。我还想知道如何确保执行特定的代码行,无论 Rails AR 对象如何实现。
  • after_initialize 是一个未广泛记录或使用的回调,与我之前的评论极为相关!
  • 根据列大小递增是一个常见的逻辑错误。您最好这样做:self.number = self.client.invoices.last.number + 1 unless self.client.nil?(发票可能会被删除,因此您最终可能会得到重复的发票编号)。
【解决方案3】:

根据上面 Jonathan R. Wallace 的评论,这里有一些关于 after_initialize 的有用讨论: http://blog.dalethatcher.com/2008/03/rails-dont-override-initialize-on.html

【讨论】:

    【解决方案4】:

    首先,你不需要使用属性集合,你可以做self.client_id。更好的是,只要您的Invoice 中有belongs_to :client,您就可以使用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
    

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

    【讨论】:

    • attributes[:client_id]self.client_id 在我尝试访问它们时均未初始化(之前调用 super;请参阅下面的 Patrick Klingemann 的回答)。 before_create 回调在这种情况下不合适,因为 @invoice.number 在视图中的 form_for @invoice 中不可见 - 在调用 @invoice.save 之前不会分配它。不过感谢您的回答!
    猜你喜欢
    • 2013-07-04
    • 1970-01-01
    • 2013-06-16
    • 1970-01-01
    • 2013-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-28
    相关资源
    最近更新 更多