Ruby-on-rails Rails after_initialize 仅适用于“new”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9610277/
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-03 02:59:23  来源:igfitidea点击:

Rails after_initialize only on "new"

ruby-on-railsmodelnested-attributes

提问by Tyler DeWitt

I have the following 2 models

我有以下 2 个模型

class Sport < ActiveRecord::Base
  has_many :charts, order: "sortWeight ASC"
  has_one :product, :as => :productable
  accepts_nested_attributes_for :product, :allow_destroy => true
end

class Product < ActiveRecord::Base
  belongs_to :category
  belongs_to :productable, :polymorphic => true
end

A sport can't exist without the product, so in my sports_controller.rbI had:

没有产品,运动就不可能存在,所以sports_controller.rb我有:

def new
  @sport = Sport.new
  @sport.product = Product.new
...
end

I tried to move the creation of the product to the sport model, using after_initialize:

我尝试将产品的创建转移到运动模型,使用after_initialize

after_initialize :create_product

def create_product
 self.product = Product.new
end

I quickly learned that after_initializeis called whenever a model is instantiated (i.e., from a findcall). So that wasn't the behavior I was looking for.

我很快了解到after_initialize每当模型被实例化时(即从find调用中)调用。所以这不是我正在寻找的行为。

Whats the way I should be modeling the requirement that all sporthave a product?

我应该如何建模所有sport具有product.

Thanks

谢谢

回答by bostonou

Putting the logic in the controller could be the best answer as you stated, but you could get the after_initializeto work by doing the following:

正如您所说,将逻辑放在控制器中可能是最好的答案,但您可以after_initialize通过执行以下操作来使其工作:

after_initialize :add_product

def add_product
  self.product ||= Product.new
end

That way, it only sets product if no product exists. It may not be worth the overhead and/or be less clear than having the logic in the controller.

这样,它只在没有产品存在时才设置产品。与控制器中的逻辑相比,它可能不值得开销和/或不那么清晰。

Edit:Per Ryan's answer, performance-wise the following would likely be better:

编辑:根据瑞安的回答,性能方面以下可能会更好:

after_initialize :add_product

def add_product
  self.product ||= Product.new if self.new_record?
end

回答by Paul Odeon

Surely after_initialize :add_product, if: :new_record?is the cleanest way here.

肯定after_initialize :add_product, if: :new_record?是这里最干净的方式。

Keep the conditional out of the add_product function

将条件排除在 add_product 函数之外

回答by Ryan

If you do self.product ||= Product.newit will still search for a product every time you do a findbecause it needs to check to see if it is nil or not. As a result it will not do any eager loading. In order to do this only when a new record is created you could simply check if it is a new record before setting the product.

如果你这样做,self.product ||= Product.new它仍然会在你每次执行时搜索一个产品,find因为它需要检查它是否为零。因此,它不会进行任何急切加载。为了仅在创建新记录时执行此操作,您可以在设置产品之前简单地检查它是否为新记录。

after_initialize :add_product

def add_product
  self.product ||= Product.new if self.new_record?
end

I did some basic benchmarking and checking if self.new_record?doesn't seem to affect performance in any noticeable way.

我做了一些基本的基准测试,检查if self.new_record?似乎不会以任何明显的方式影响性能。

回答by James Brooks

Instead of using after_initialize, how about after_create?

而不是使用after_initialize,怎么样after_create

after_create :create_product

def create_product
  self.product = Product.new
  save
end

Does that look like it would solve your issue?

这看起来能解决你的问题吗?

回答by coderates

It looks like you are very close. You should be able to do away with the after_initialize call altogether, but first I believe if your Sport model has a "has_one" relationship with :product as you've indicated, then your Product model should also "belong_to" sport. Add this to your Product model

看起来你们很亲近。您应该能够完全取消 after_initialize 调用,但首先我相信如果您的 Sport 模型与 :product 有“has_one”关系,那么您的 Product 模型也应该“belong_to”运动。将此添加到您的产品模型中

belongs_to: :sport

Next step, you should now be able to instantiate a Sport model like so

下一步,您现在应该能够像这样实例化运动模型

@sport = @product.sport.create( ... )

This is based off the information from Association Basicsfrom Ruby on Rails Guides, which you could have a read through if I am not exactly correct

这是基于Ruby on Rails 指南中的Association Basics 中的信息,如果我不完全正确,您可以通读一下

回答by Victor Nazarov

You should just override initialize method like

您应该只覆盖 initialize 方法,例如

class Sport < ActiveRecord::Base

  # ...

  def initialize(attributes = {})
    super
    self.build_product
    self.attributes = attributes
  end

  # ...

end

Initialize method is never called when record is loaded from database. Notice that in the code above attributes are assigned after product is build. In such setting attribute assignment can affect created product instance.

从数据库加载记录时,从不调用 Initialize 方法。请注意,在上面的代码中,属性是在产品构建后分配的。在这样的设置中,属性分配会影响创建的产品实例。