如何覆盖rails模型的“new”方法
在我的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]) }
但是,我想保持我的控制器瘦,加上我想保持模型逻辑不在其中。 我猜我需要覆盖模型中的“新”方法。 最好的方法是什么?
正如@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
不要覆盖initialize
它可能会破坏模型中的很多东西。 如果我们知道为什么你需要我们可以帮助更好(不完全理解你对forms的解释是骨架,你想要表单属性来创建其他属性??见下文)。 正如马塞尔建议的那样,我经常使用钩子。 但是,如果您希望它始终发生,而不是在创建或保存对象之前,请使用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_initialize
或some_attribute=
然后在模型上创建其他属性。
我想你想为你的模型属性设置默认值?
除了重写之外,还有另一种解决方案; 你可以设置回调:
class Model before_create :default_values def default_values ... end
为什么不像这样在模型中创建一个方法呢?
def self.create_dates(params) [...] end
包含这个逻辑(基本上是你的循环?)
您可以使用:
def initialize(attributes = nil) # do your stuff... end
虽然在某处我读到它并不值得推荐……
这种工厂方法模式的臭气……寻找它。
如果你因某些原因不愿意为@Pasta使用create_date,那么可能只创建一个简单的ruby对象(不支持ActiveRecord),名为YourModelFactory / Template /无论有两个实例变量 – 你可以使用你的标准参数[:foo] ]分配这些 – 然后在该类上定义并调用一个返回真实对象的方法。
您的控制器逻辑现在看起来像这样:
mmf = MyModelFactory.new(params[:foo]) objs = mmf.create_real_deal_models
祝好运。
严格来说,尽管很晚,在模型中覆盖新的正确方法是
def initialize(args) # # do whatever, args are passed to super # super end