我正在将项目从MongoMapper迁移到Mongoid。我有两个模型Graph
和Point
,其中引用了1-N关系,因此点belongs_to :graph
和每个图has_many :points
。我已经在Point模型中覆盖了初始化器,因为我需要从该点所属的图形实例中引用一些状态,以便正确初始化该点。
然而,似乎即使在初始化程序中调用super
之后关系也未初始化。如何正确设置点的状态?
这是我的两个类和初始化程序的简化版本:
class Graph
include Mongoid::Document
has_many :points, dependent: :delete
field :type, type: String
field :timezone, type: String
# ...etc...
end
class Point
include Mongoid::Document
belongs_to :graph
field :date, type: Date
field :value, type: Float
field :urtext, type: String # as originally entered by user
field :nextday, type: Date
attr_accessible :urtext
validates :value, presence: true
validates :date, presence: true
def initialize(params, options={})
super(params, options)
parse_urtext
end
def parse_urtext(ur)
# for the sake of argument, imagine this is doing some critical calculation
# that requires info derived from current state of the graph at creation
# and which is related to parsing the urtext
self.date, self.value = urtext.split(",")
self.nextday = Time.use_zone(self.graph.timezone){ Time.zone.now.to_date + 1 }
end
end
当我尝试创建与图表关联的新点时,我从初始化程序中得到了一个nil错误。
# in a console:
> g = Graph.first
> p = g.points.build( urtext: "2015-11-30,1" )
NoMethodError: undefined method `timezone' for nil:NilClass
from /rails_root/app/models/point.rb:34:in `parse_urtext'
from /rails_root/app/models/point.rb:22:in `initialize'
from /.../.rvm/gems/ruby-2.2.0/gems/mongoid-3.1.7/lib/mongoid/factory.rb:23:in `new'
...
任何人都可以解释为什么这不起作用?我可以在Graph模型上编写一个build_point方法,并始终以这种方式创建Points,但后来我失去了遵循Rails约定的好处。回调似乎并没有让我得到我想要的行为。 (验证是首先运行的,我想在验证之前完成初始化状态)。
Rails 3.2.22,Mongoid 3.1.7
答案 0 :(得分:1)
你错了。使用O [RD] M时覆盖initialize
几乎总是一个坏主意。
更常见的方法是使用标准生命周期钩子来解析事物。例如,您可以使用before_validation
:
class Point
include Mongoid::Document
belongs_to :graph
field :date, type: Date
field :value, type: Float
field :urtext, type: String
field :nextday, type: Date
before_validation :parse_urtext, :if => :urtext_changed?
validates :value, presence: true
validates :date, presence: true
private
def parse_urtext
self.date, self.value = self.urtext.split(",")
self.nextday = Time.use_zone(self.graph.timezone){ Time.zone.now.to_date + 1 }
end
end
在某些情况下,您可以覆盖urtext
mutator方法:
def urtext=(v)
...
end
但这不会在这里工作。说g.points.build( urtext: "2015-11-30,1" )
会调用#urtext=
,但不一定会调用#graph_id=
,因此在self.graph.nil?
调用期间,您可以(在这种情况下实际上)#urtext=
g.points.build(...)
。
我倾向于使用before_validation
挂钩进行此类操作,然后validates
挂钩以确保before_validation
调用做了正确的事情。