开发者

How to override "new" method for a rails model

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

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) { m开发者_开发问答y_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?


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:

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


Don't override initialize It 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_initialize hook.

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_attribute corresponds with the column name of your model 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_initialize or some_attribute= to then create other attributes on your model.


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

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


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


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?)


You can use:

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

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


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.

Your controller logic now looks something like this:

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

Good luck.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜