如何覆盖rails模型的“new”方法

时间:2010-12-07 13:18:17

标签: ruby-on-rails

在我的rails应用程序中,我有一个带有start_date和end_date的模型。如果用户选择2010年1月1日作为start_date并将2010年1月5日作为end_date,我希望创建5个模型实例(每天选择一个)。所以它看起来像

Jan 1, 2010
Jan 2, 2010
Jan 3, 2010
Jan 4, 2010
Jan 5, 2010

我知道处理这种情况的一种方法是在控制器中进行循环。有点像...

# ...inside controller
start_date.upto(end_date) { my_model.new(params[:my_model]) }

但是,我想保持我的控制器瘦,加上我想保持模型逻辑不在其中。我猜我需要覆盖模型中的“新”方法。最好的方法是什么?

7 个答案:

答案 0 :(得分:17)

正如@brad所说,你绝对不想覆盖初始化。虽然你可以覆盖after_initialize,但这并不像你想要的那样。相反,您可能想要像@Pasta建议的那样向类中添加工厂方法。所以将它添加到您的模型中:

def self.build_for_range(start_date, end_date, attributes={})
  start_date.upto(end_date).map { new(attributes) }
end

然后将其添加到您的控制器:

models = MyModel.build_for_range(start_date, end_date, params[:my_model])
if models.all?(:valid?)
  models.each(&:save)
  # redirect the user somewhere ...
end

答案 1 :(得分:10)

不要覆盖initialize它可能会破坏模型中的很多东西。如果我们知道为什么你需要我们可以帮助更好(不完全理解你对形式的解释是骨架,你想要表单属性来创建其他属性??见下文)。正如马塞尔建议的那样,我经常使用钩子。但是,如果您希望它始终发生,而不是在创建或保存对象之前,请使用after_initialize挂钩。

def after_initialize
  # Gets called right after Model.new
  # Do some stuff here
end

此外,如果您只是寻找一些默认值,您可以提供默认访问器,例如:(其中some_attribute与您的模型属性的列名对应)

def some_attribute
  attributes[:some_attribute] || "Some Default Value"
end

或作家

def some_attribute=(something)
  attributes[:some_attribute] = something.with_some_changes
end

如果我正确理解您的评论,看起来您公开的表单会使您的模型不完整,其他属性基于此表单的某些部分?在这种情况下,您可以使用上述任何方法after_initializesome_attribute=,然后在模型上创建其他属性。

答案 2 :(得分:2)

为什么不像这样在模型中创建一个方法

 def self.create_dates(params) 
   [...] 
  end

包含这个逻辑(基本上是你的循环?)

答案 3 :(得分:2)

我想你想为你的模型属性设置默认值吗?

除了重写之外还有另一种解决方案;你可以设置回调:

class Model

before_create :default_values
def default_values
  ...
end

答案 4 :(得分:2)

您可以使用:

def initialize(attributes = nil)
  # do your stuff...
end

虽然在某处我读到它并不值得推荐......

答案 5 :(得分:2)

这种工厂方法模式的臭气......寻求它。

如果你因为某种原因不愿意为每个@Pasta使用create_date,那么可能只创建一个简单的ruby对象(不支持ActiveRecord),名为YourModelFactory / Template / Whatever with two vars - 你可以使用你的标准参数[:foo]分配这些 - 然后在该类上定义并调用一个返回真实对象的方法。

您的控制器逻辑现在看起来像这样:

mmf  = MyModelFactory.new(params[:foo])
objs = mmf.create_real_deal_models
祝你好运。

答案 6 :(得分:2)

严格来说,虽然很晚,但在模型中覆盖 new 的正确方法是

def initialize(args)
    #
    # do whatever, args are passed to super
    #
    super
end