Ruby-on-rails 如何覆盖 Rails 模型的“new”方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4376992/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 23:49:42  来源:igfitidea点击:

How to override "new" method for a rails model

ruby-on-rails

提问by Lan

In my rails app I have a model with a start_date and end_date. If the user selects Jan 1, 2010 as the start_date and Jan 5, 2010 as the end_date, I want there to be 5 instances of my model created (one for each day selected). So it'll look something like

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

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

I know one way to handle this is to do a loop in the controller. Something like...

我知道处理这个问题的一种方法是在控制器中做一个循环。就像是...

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

However, I want to keep my controller skinny, plus I want to keep the model logic outside of it. I'm guessing I need to override the "new" method in the model. What's the best way to do this?

但是,我想保持我的控制器瘦,而且我想将模型逻辑保留在它之外。我猜我需要覆盖模型中的“新”方法。做到这一点的最佳方法是什么?

回答by Trotter

As @brad says, you definitely do not want to override initialize. Though you could override after_initialize, that doesn't really look like what you want here. Instead, you probably want to add a factory method to the class like @Pasta suggests. So add this to your model:

正如@brad 所说,您绝对不想覆盖初始化。尽管您可以覆盖 after_initialize,但这看起来并不像您在这里想要的那样。相反,您可能希望像@Pasta 建议的那样向类添加工厂方法。因此,将其添加到您的模型中:

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

And then add this to your controller:

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

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

回答by brad

Don't override initializeIt could possibly break a lot of stuff in your models. IF we knew why you needed to we could help better ( don't fully understand your explanation of the form being a skeleton, you want form attributes to create other attributes?? see below). I often use a hook as Marcel suggested. But if you want it to happen all the time, not just before you create or save an object, use the after_initializehook.

不要覆盖initialize它可能会破坏模型中的很多东西。如果我们知道您为什么需要我们可以提供更好的帮助(不完全理解您对表单是骨架的解释,您希望表单属性创建其他属性?见下文)。我经常按照 Marcel 的建议使用钩子。但是,如果您希望它一直发生,而不仅仅是在创建或保存对象之前,请使用after_initialize钩子。

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

Also if you're just looking for some default values you can provide default accessors, something like: (where some_attributecorresponds with the column name of your model attribute)

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

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

or a writer

或作家

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

If I understand your comment correctly, it looks like you expose a form that would make your model incomplete, with the other attributes based on parts of this form? In this case you can use any of the above methods after_initializeor some_attribute=to then create other attributes on your model.

如果我正确理解您的评论,您似乎公开了一个会使您的模型不完整的表单,而其他属性则基于此表单的一部分?在这种情况下,您可以使用上述任何方法after_initializesome_attribute=在模型上创建其他属性。

回答by s1mpl3

Strictly, although late, the proper way to override newin a model is

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

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

回答by Cory

This reeks of the factory method patttern...seek it out.

这种工厂方法模式的恶臭......寻找它。

If you're reluctant for some reason to go with create_date per @Pasta, then possibly create just a simple ruby object (not ActiveRecord backed), named YourModelFactory/Template/Whatever with two instance vars - you can use your standard params[:foo] to assign these - then define and call a method on that class that returns your real objects.

如果您出于某种原因不愿意使用每个@Pasta 的 create_date,那么可能只创建一个简单的 ruby​​ 对象(不支持 ActiveRecord),名为 YourModelFactory/Template/Whatever 并带有两个实例变量 - 您可以使用您的标准 params[:foo ] 来分配这些 - 然后在该类上定义并调用一个返回真实对象的方法。

Your controller logic now looks something like this:

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

mmf  = MyModelFactory.new(params[:foo])
objs = mmf.create_real_deal_models

Good luck.

祝你好运。

回答by Pasta

why don't you just create a method into your model like this

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

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

containing this logic (basically your loop?)

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

回答by Marcel Falliere

I guess you want to set default values for your model attribute ?

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

There's another solution than overriding ; you can set callbacks :

除了覆盖之外还有另一种解决方案;您可以设置回调:

class Model

before_create :default_values
def default_values
  ...
end

回答by jordinl

You can use:

您可以使用:

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

Although somewhere I read it wasn't recommendable...

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